如何使用 ASP.NET Core 更改输入字段的客户端验证错误 CSS 类?

Way*_*yne 3 c# jquery asp.net-core

我有一个带有客户端验证的表单,当检测到错误输入时,输入字段的类属性会发生更改;它被更改为包括"input-validation-error"类。

我想更改这个类,以便不使用它而是使用 Bootstraps class "is-invalid"

我尝试使用 ASP.NET Core 的TagHelpers,但这没有效果;我相信这不会起作用,因为助手只有在加载“整个页面”时才会起作用,它对客户端验证没有帮助。

当我在 .NET 项目中搜索时,会发现 .NET 项目中定义的 css 类"Unobtrusive validation support library for jQuery"

更改此类的最佳方法是什么?

CSS 能否通过将一个类更改为另一个类来提供帮助? (覆盖原来的类,不确定这是否可能)

或者应该使用 JavaScript 来重新配置JQuery

这是我的 TagHelper,添加了助手:validation-for,validation-error-class,validation-valid-class

表单/HTML...

<input type="email" asp-for="Email" id="inputEmail" class="form-control" placeholder="Email address" required
                            validation-for="Email" validation-error-class="is-invalid" validation-valid-class="is-valid"/>
                            <span class="small" asp-validation-for="Email"></span>
Run Code Online (Sandbox Code Playgroud)

这是我的 TagHelper 的代码片段。

[HtmlTargetElement("input", Attributes = "validation-for,validation-error-class,validation-valid-class")]
public class ValidationErrorClassTagHelper : TagHelper
{
    [HtmlAttributeName("validation-for")]
    public ModelExpression For { get; set; }

    [HtmlAttributeName("validation-error-class")]
    public string ErrorClass { get; set; }
    [HtmlAttributeName("validation-valid-class")]
    public string ValidClass { get; set; }


    [HtmlAttributeNotBound]
    [ViewContext]
    public ViewContext ViewContext { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.RemoveClass(ErrorClass,HtmlEncoder.Default);
        output.RemoveClass(ValidClass,HtmlEncoder.Default);

        if (ViewContext.ViewData.ModelState.IsValid) {                
            output.AddClass(ValidClass,HtmlEncoder.Default);
        } else 
        {
            output.AddClass(ErrorClass,HtmlEncoder.Default);                
        }          
    }
}
Run Code Online (Sandbox Code Playgroud)

新方法没有 100% 有效。

我尝试了另一种方法,通过修改jQuery defaultOptions、更改 errorClass 和 validClass。

在 [ https://github.com/brecons/jquery-validation-unobtrusive-bootstrap][gitHub]上找到的代码片段

function ($) {
    if($.validator && $.validator.unobtrusive){
        var defaultOptions = {
            validClass: 'is-valid',
            errorClass: 'is-invalid',
Run Code Online (Sandbox Code Playgroud)

这适用于 errorClass,但对我来说 validClass 保持不变,它仍然被命名为valid

Ada*_*mon 8

我也遇到过这个问题,但我不太喜欢在客户端修复它的想法,因为 jQuery 技巧仅在浏览器中启用 JavaScript 时才起作用。因此,我认为这个问题应该在服务器端解决。

不幸的是,该框架没有提供配置验证相关CSS类的方法,这些字符串只是硬编码的

但是,这些字段不是常量,而是声明为静态的,因此我们可以在运行时通过反射更改它们的值(就像此处介绍的那样)。但这些都是肮脏的黑客行为,这应该是我们最后的手段。

OP 的自定义标签助手的想法看起来好多了。但它有一个缺点:它只能修复标签助手生成的标记。经典的Html.TextBox(...)类似方法仍然会被破坏。

那么,我们还能做得更好吗?幸运的是,是的!

Html标签帮助器实现都使用IHtmlGenerator底层服务来生成标记。得益于 ASP.NET Core 的模块化架构,我们可以提供该服务的定制版本。(更重要的是,我们甚至可以在不复制一堆代码的情况下做到这一点,因为默认实现将相关方法声明为虚拟方法。)

因此,我可以想出这个解决方案:

public sealed class CustomHtmlGenerator : DefaultHtmlGenerator
{
    private static IHtmlHelper GetHtmlHelperFor(ViewContext viewContext)
    {
        const string htmlHelperViewDataKey = nameof(CustomHtmlGenerator) + "_" + nameof(IHtmlHelper);

        if (!viewContext.ViewData.TryGetValue(htmlHelperViewDataKey, out var htmlHelperObj) || !(htmlHelperObj is IHtmlHelper htmlHelper))
            viewContext.ViewData[htmlHelperViewDataKey] = htmlHelper = GetViewHtmlHelper(viewContext) ?? CreateHtmlHelper(viewContext);

        return htmlHelper;

        static IHtmlHelper GetViewHtmlHelper(ViewContext viewContext)
        {
            if (!(viewContext.View is RazorView razorView))
                return null;

            dynamic razorPage = razorView.RazorPage;

            try { return (IHtmlHelper)razorPage.Html; }
            catch { return null; }
        }

        static IHtmlHelper CreateHtmlHelper(ViewContext viewContext)
        {
            var htmlHelper = viewContext.HttpContext.RequestServices.GetRequiredService<IHtmlHelper>();
            (htmlHelper as IViewContextAware)?.Contextualize(viewContext);
            return htmlHelper;
        }
    }

    private static TagBuilder AddBootstrapValidationCssClasses(ViewContext viewContext, string expression, TagBuilder tagBuilder)
    {
        // we need to get the model property key from the expression, which functionality is buried in an internal class unfortunately
        // (https://github.com/dotnet/aspnetcore/blob/v3.1.6/src/Mvc/Mvc.ViewFeatures/src/NameAndIdProvider.cs#L147)
        // however, this internal API is exposed via the IHtmlHelper.Name method:
        // (https://github.com/dotnet/aspnetcore/blob/v3.1.6/src/Mvc/Mvc.ViewFeatures/src/HtmlHelper.cs#L451)
        var htmlHelper = GetHtmlHelperFor(viewContext);
        var fullName = htmlHelper.Name(expression);

        if (viewContext.ModelState.TryGetValue(fullName, out var entry))
        {
            if (entry.ValidationState == ModelValidationState.Invalid)
                tagBuilder.AddCssClass("is-invalid");
            else if (entry.ValidationState == ModelValidationState.Valid)
                tagBuilder.AddCssClass("is-valid");
        }

        return tagBuilder;
    }

    public CustomHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, ValidationHtmlAttributeProvider validationAttributeProvider)
        : base(antiforgery, optionsAccessor, metadataProvider, urlHelperFactory, htmlEncoder, validationAttributeProvider) { }

    protected override TagBuilder GenerateInput(ViewContext viewContext, InputType inputType, ModelExplorer modelExplorer, string expression, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes) =>
        AddBootstrapValidationCssClasses(viewContext, expression, base.GenerateInput(viewContext, inputType, modelExplorer, expression, value, useViewData, isChecked, setId, isExplicitValue, format, htmlAttributes));

    public override TagBuilder GenerateSelect(ViewContext viewContext, ModelExplorer modelExplorer, string optionLabel, string expression, IEnumerable<SelectListItem> selectList, ICollection<string> currentValues, bool allowMultiple, object htmlAttributes) =>
        AddBootstrapValidationCssClasses(viewContext, expression, base.GenerateSelect(viewContext, modelExplorer, optionLabel, expression, selectList, currentValues, allowMultiple, htmlAttributes));

    public override TagBuilder GenerateTextArea(ViewContext viewContext, ModelExplorer modelExplorer, string expression, int rows, int columns, object htmlAttributes) =>
        AddBootstrapValidationCssClasses(viewContext, expression, base.GenerateTextArea(viewContext, modelExplorer, expression, rows, columns, htmlAttributes));
}
Run Code Online (Sandbox Code Playgroud)

剩下的就是通过在方法末尾添加以下内容来配置 DI 来解析此自定义实现Startup.ConfigureService

services.Replace(ServiceDescriptor.Singleton<IHtmlGenerator, CustomHtmlGenerator>());
Run Code Online (Sandbox Code Playgroud)