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
。
我也遇到过这个问题,但我不太喜欢在客户端修复它的想法,因为 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)