9

The following code:

View:(is-valid)

<div class="form-group">
    @Html.LabelFor(m => m.Telefone, new { @class = "font-weight-bold" })
    @Html.TextBoxFor(m => m.Telefone, new { @class = "form-control is-valid", @placeholder = "Digite seu telefone" })
    @Html.ValidationMessageFor(m => m.Telefone, "", new { @class = "text-danger" })
</div>

View:(is-invalid)

<div class="form-group">
    @Html.LabelFor(m => m.Telefone, new { @class = "font-weight-bold" })
    @Html.TextBoxFor(m => m.Telefone, new { @class = "form-control is-invalid", @placeholder = "Digite seu telefone" })
    @Html.ValidationMessageFor(m => m.Telefone, "", new { @class = "text-danger" })
</div>

Example: https://getbootstrap.com/docs/4.3/components/forms/#server-side

Any solution ?

1

8 Answers 8

10

The class name is hardcoded: https://github.com/dotnet/aspnetcore/blob/v3.1.6/src/Mvc/Mvc.ViewFeatures/src/HtmlHelper.cs#L25

The only option is to alter CSS.

When you build Bootstrap from sources you can just add the next code into your SCSS file:

.input-validation-error {
  @extend .is-invalid;
}

This will create an alias for existing .is-invalid.

Sign up to request clarification or add additional context in comments.

1 Comment

For those searching, this works with .NET 5 and Bootstrap 5.
8

Simple solution by using Tag Helpers:

        <div class="form-group">
            <input asp-for="Email" class="form-control">
            <div class="invalid-feedback" style="display:block;">
                <span asp-validation-for="Email"></span>
            </div>
        </div>

1 Comment

What if you want to change the Input box itself using the is-vailid or is-invalid so it shows green or red? "<input type="text" class="form-control is-invalid" >"
1

I leave here two solutions that have not been mentioned before. One dogmatic and one pragmatic, but depending on what you are looking for both solutions can be interesting. With both you will solve the problem in 1 minute.

Proof of concept:

enter image description here

Brief introduction (really necessary):

Microsoft uses jquery-validation to perform client-side validation, and has extended the functionality of this library with its own jquery-validation-unobtrusive library.

If you go to the wwwroot/lib/jquery-validation-unobtrusive folder within the project you will find that within this library are rules that add classes such as “input-validation-error”.

Solution 1:

If we inspect the uses of, we see that “errorClass” is first queried from the options, and if it does not exist, then “input-validation-error” is used by default.

enter image description here

The first solution is to generate the configuration in which we are the ones who indicate the name of “errorClass”.

Include the following script in your execution tree:

<script>
    const settings = {
        errorClass: 'is-invalid',
    }
    $.validator.unobtrusive.options = settings
</script>

Good place to do this would be at the end of your _ValidationScriptsPartial.cshtml file. Either way, you have to make sure that your script is executed after the inclusion of the jquery.validate.unobtrusive.js file.

Solution 2:

The solution is as simple as replacing the string “input-validation-error” with “is-invalid” (there are two places).

enter image description here

Why do I consider these solutions to be the cleanest and simplest?

  • There is no need to add Extensions, Helpers or magic Services that you have to rethink later (you always have to adjust the code, or update the .Net version, or include a new dev).

  • This solutions works transparently with any Razor project. The dev should not even notice this tweak. He should just write his Razor components.

  • It does not introduce weird rules with JS, which later in the execution chain can be difficult to debug.

  • It does not set up new styles, nor magic inheritances to solve the problem, which usually causes headaches when styles do not work properly.

  • Finally, this solution not only works for Bootstrap, it works for any stylesheet you want to use. It avoids name dependency with Microsoft defined classes (apparently, without being consistent in this aspect, so I don't see any good reason to keep using them).

1 Comment

Thank your for you detailed explanation
0

Razor uses jQuery validation. You only need to hock jq-valid with Bootstrap:

$('form').validate().settings.errorClass += ' is-invalid';
$('form').validate().settings.validClass += ' is-valid';

Comments

0

As @seagull and @cesar mentioned, the default class ASP.NET uses for form validation errors is input-validation-error. This shows up in both client-side validation (via the jQuery Unobtrusive Validation library) and server-side (via the HtmlHelper class).

Therefore, both client-side and server-side validation need to be addressed for a complete solution.

Client-side

Update the default _ValidationScriptsPartial.cshtml file like so:

<script src="~/lib/jquery-validate/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script>
    // Change validation classes to work with Bootstrap
    const settings = {
        validClass: "is-valid",
        errorClass: "is-invalid"
    };
    $.validator.setDefaults(settings);
    $.validator.unobtrusive.options = settings;
</script>

This allows the validation JavaScript to run as normal, but swaps in the correct Bootstrap classes. (Depending on the template you used, the paths on the first two line might need to be changed.)

Server-side

Because the class on the server side is hardcoded and can't be changed, we need to update the classes after validation runs. This can be done using a custom Tag Helper. Create a file (named InvalidClassTagHelper.cs) in your project with the following code:

using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Text.Encodings.Web;

/// <summary>
/// This <see cref="ITagHelper"/> implementation adds the Bootstrap-compatible class "is-invalid" to form elements
/// <c>input</c>, <c>select</c>, and <c>textarea</c> if they have already been marked as invalid with the ASP.NET
/// default class "input-validation-error".
/// </summary>
[HtmlTargetElement("input", Attributes = ForAttributeName)]
[HtmlTargetElement("select", Attributes = ForAttributeName)]
[HtmlTargetElement("textarea", Attributes = ForAttributeName)]
public class InvalidClassTagHelper : TagHelper
{
    private const string ForAttributeName = "asp-for";
    private const string ClassAttributeName = "class";
    private const string BootstrapInvalidElementClass = "is-invalid";

    [HtmlAttributeName(ForAttributeName)]
    public ModelExpression? For { get; set; }
    
    // If the element contains the default ASP.NET class indicating a validation error,
    // this will add the corresponding Bootstrap class.
    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        base.Process(context, output);

        if (output.Attributes.TryGetAttribute(ClassAttributeName, out var classAttribute) &&
            ExtractClassValue(classAttribute).Contains(HtmlHelper.ValidationInputCssClassName))
        {
            output.AddClass(BootstrapInvalidElementClass, HtmlEncoder.Default);
        }
    }

    // Gets the current value of the element's class attribute.
    // Adapted from https://github.com/dotnet/aspnetcore/blob/ab9e2630e6144efc529bfa8c67caa68732c80086/src/Mvc/Mvc.TagHelpers/src/TagHelperOutputExtensions.cs#L257-L283
    private static string ExtractClassValue(TagHelperAttribute classAttribute)
    {
        string? extractedClassValue;

        switch (classAttribute.Value)
        {
            case string valueAsString:
                extractedClassValue = HtmlEncoder.Default.Encode(valueAsString);
                break;
            case HtmlString valueAsHtmlString:
                extractedClassValue = valueAsHtmlString.Value;
                break;
            case IHtmlContent valueAsHtmlContent:
                using (var stringWriter = new StringWriter())
                {
                    valueAsHtmlContent.WriteTo(stringWriter, HtmlEncoder.Default);
                    extractedClassValue = stringWriter.ToString();
                }

                break;
            default:
                extractedClassValue = null;
                break;
        }

        return extractedClassValue ?? string.Empty;
    }
}

The Tag Helper checks <input>, <select>, and <textarea> elements for the default validation error class, and if present, it adds the Bootstrap-compatible class as well.

Comments

-1
//CONFIGURACAO BOOTSTRAP 4 PARA JQUERY VALIDATION PLUGIN
jQuery.validator.setDefaults({
    onfocusout: function (e) {
        this.element(e);
    },
    //onkeyup: false,
    highlight: function (element) {
        jQuery(element).closest('.form-control').addClass('is-invalid');
    },
    unhighlight: function (element) {
        jQuery(element).closest('.form-control').removeClass('is-invalid');
        jQuery(element).closest('.form-control').addClass('is-valid');
    },

    errorElement: 'div',
    errorClass: 'invalid-feedback',
    errorPlacement: function (error, element) {
        if (element.parent('.input-group-prepend').length) {
            $(element).siblings(".invalid-feedback").append(error);
            //error.insertAfter(element.parent());
        } else {
            error.insertAfter(element);
        }
    },
});

1 Comment

verbal explanations are often very helpful in answers
-1

Building off Seagull's answer for .Net 5 and Bootstrap 5 (I can't comment on his answer).

I was able to follow this guide, https://www.dotnetcatch.com/2019/05/16/adding-bootstrap-sass-to-asp-net-core/, to build a custom Bootstrap CSS that includes the ASP.NET validation class name so that you get the look of the Bootstrap validation controls.

@import "../lib/bootstrap/scss/bootstrap";

.input-validation-error {
    @extend .is-invalid;
}

Comments

-4

be sure to include : "jquery .js jquery.validate .js jquery.validate.unobtrusive .js"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.