c# - 如何使用 ASP.NET Core 更改输入字段的客户端验证错误 CSS 类?
问题描述
我有一个带有客户端验证的表单,当检测到错误输入时,会更改输入字段的类属性;它被更改为包括"input-validation-error"
类。
我想更改此类,以便不使用它,而是使用 Bootstraps 类"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>
这是我的 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);
}
}
}
新方法不能 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',
这适用于errorClass,但对我来说validClass保持不变,它仍然被命名valid
。
解决方案
我也遇到过这个问题,但我真的不喜欢在客户端修复它的想法,因为 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));
}
剩下的就是通过在方法末尾添加以下内容来配置 DI 以解决此自定义实现Startup.ConfigureService
:
services.Replace(ServiceDescriptor.Singleton<IHtmlGenerator, CustomHtmlGenerator>());
推荐阅读
- swiftui - SwiftUI - 子类视图模型不会触发视图刷新
- javascript - 如何在不预编译的情况下导入@material/mwc-button?
- python - 如何从不同的数据框中指定训练集和测试集?
- javascript - 我的动画在我使用 html、css、js 制作的这个滑块中不起作用
- java - 差点拿到不同IV的原文,正常吗?
- spring - 为什么 Mockito 和 Mockmvc 不能一起工作
- snowflake-cloud-data-platform - 雪花查询配置文件接口
- angular - Angular 9:无法读取从 API 调用返回的嵌套对象中的值 [未定义]
- c - 如何在我的 Windows 内核驱动程序中访问我的用户模式 thread_local 变量?
- pandoc - 将降价列表转换为 html 的意外 pandoc 行为