-
-
Save FWest98/9141b3c2f260bee0e46058897d2017d2 to your computer and use it in GitHub Desktop.
| // This is the default partial generated in the template, if you have another place | |
| // where you include the jQuery unobtrusive validation plugin, put this code there | |
| @using Foo | |
| @inject IOptions<ValidationOptions> validationOptionsHolder | |
| @{ var validationOptions = validationOptionsHolder.Value; } | |
| // Here is your normal environment stuff | |
| <script> | |
| (function () { | |
| // Set default options for the validation | |
| const defaultOptions = { | |
| errorClass: "@validationOptions.InvalidInput", // or add custom, extra ones | |
| validClass: "@validationOptions.ValidInput" | |
| }; | |
| $.validator.setDefaults(defaultOptions); | |
| $.validator.unobtrusive.options = defaultOptions; | |
| })(); | |
| </script> |
| using System.Collections.Generic; | |
| using System.Text.Encodings.Web; | |
| using Microsoft.AspNetCore.Antiforgery; | |
| using Microsoft.AspNetCore.Mvc; | |
| using Microsoft.AspNetCore.Mvc.ModelBinding; | |
| using Microsoft.AspNetCore.Mvc.Rendering; | |
| using Microsoft.AspNetCore.Mvc.Routing; | |
| using Microsoft.AspNetCore.Mvc.ViewFeatures; | |
| using Microsoft.Extensions.Options; | |
| namespace Foo | |
| { | |
| // Custom HTML Generator to override the default behaviour and replace the CSS classes as they emerge | |
| public class CustomHtmlGenerator : DefaultHtmlGenerator | |
| { | |
| private readonly ValidationOptions _options; | |
| public CustomHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, | |
| IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, | |
| ValidationHtmlAttributeProvider validationAttributeProvider, IOptions<ValidationOptions> options) : | |
| base(antiforgery, optionsAccessor, metadataProvider, urlHelperFactory, htmlEncoder, validationAttributeProvider) | |
| { | |
| _options = options.Value; | |
| } | |
| 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) | |
| { | |
| var tagBuilder = base.GenerateInput(viewContext, inputType, modelExplorer, expression, value, useViewData, isChecked, setId, isExplicitValue, format, htmlAttributes); | |
| FixValidationCssClassNames(tagBuilder); | |
| return tagBuilder; | |
| } | |
| public override TagBuilder GenerateTextArea(ViewContext viewContext, ModelExplorer modelExplorer, string expression, int rows, | |
| int columns, object htmlAttributes) | |
| { | |
| var tagBuilder = base.GenerateTextArea(viewContext, modelExplorer, expression, rows, columns, htmlAttributes); | |
| FixValidationCssClassNames(tagBuilder); | |
| return tagBuilder; | |
| } | |
| public override TagBuilder GenerateValidationMessage(ViewContext viewContext, ModelExplorer modelExplorer, string expression, | |
| string message, string tag, object htmlAttributes) | |
| { | |
| var tagBuilder = base.GenerateValidationMessage(viewContext, modelExplorer, expression, message, tag, htmlAttributes); | |
| FixValidationCssClassNames(tagBuilder); | |
| return tagBuilder; | |
| } | |
| public override TagBuilder GenerateValidationSummary(ViewContext viewContext, bool excludePropertyErrors, string message, | |
| string headerTag, object htmlAttributes) | |
| { | |
| var tagBuilder = base.GenerateValidationSummary(viewContext, excludePropertyErrors, message, headerTag, htmlAttributes); | |
| FixValidationCssClassNames(tagBuilder); | |
| return tagBuilder; | |
| } | |
| private void FixValidationCssClassNames(TagBuilder tagBuilder) | |
| { | |
| tagBuilder.ReplaceCssClass(HtmlHelper.ValidationInputCssClassName, _options.InvalidInput); | |
| tagBuilder.ReplaceCssClass(HtmlHelper.ValidationInputValidCssClassName, _options.ValidInput); | |
| tagBuilder.ReplaceCssClass(HtmlHelper.ValidationMessageCssClassName, _options.InvalidMessage); | |
| tagBuilder.ReplaceCssClass(HtmlHelper.ValidationMessageValidCssClassName, _options.ValidMessage); | |
| tagBuilder.ReplaceCssClass(HtmlHelper.ValidationSummaryCssClassName, _options.InvalidSummary); | |
| tagBuilder.ReplaceCssClass(HtmlHelper.ValidationSummaryValidCssClassName, _options.ValidSummary); | |
| } | |
| } | |
| public static class TagBuilderHelpers | |
| { | |
| public static void ReplaceCssClass(this TagBuilder tagBuilder, string old, string val) | |
| { | |
| if (!tagBuilder.Attributes.TryGetValue("class", out string str)) return; | |
| tagBuilder.Attributes["class"] = str.Replace(old, val); | |
| } | |
| } | |
| } |
| using System; | |
| using System.DirectoryServices.AccountManagement; | |
| using System.Linq; | |
| using Microsoft.AspNetCore.Authentication.Cookies; | |
| using Microsoft.AspNetCore.Authentication.WsFederation; | |
| using Microsoft.AspNetCore.Builder; | |
| using Microsoft.AspNetCore.Hosting; | |
| using Microsoft.AspNetCore.Mvc.DataAnnotations; | |
| using Microsoft.AspNetCore.Mvc.ViewFeatures; | |
| using Microsoft.EntityFrameworkCore; | |
| using Microsoft.Extensions.Configuration; | |
| using Microsoft.Extensions.DependencyInjection; | |
| using Microsoft.Extensions.Hosting; | |
| using Microsoft.Extensions.Options; | |
| namespace Foo | |
| { | |
| public class Startup | |
| { | |
| public Startup(IConfiguration configuration) | |
| { | |
| Configuration = configuration; | |
| } | |
| public IConfiguration Configuration { get; } | |
| // This method gets called by the runtime. Use this method to add services to the container. | |
| public void ConfigureServices(IServiceCollection services) | |
| { | |
| // All your usual stuff | |
| // Configure Options | |
| services.Configure<ValidationOptions>(Configuration.GetSection("Validation")); // or use a custom source | |
| // Configure custom html generator to override css classnames | |
| services.AddSingleton<IHtmlGenerator, CustomHtmlGenerator>(); | |
| } | |
| } | |
| } |
| namespace Foo | |
| { | |
| // Options class to set the validation classes. Of course, this is not required as you can | |
| // also harcode the classnames in the CustomHtmlGenerator file. | |
| public class ValidationOptions | |
| { | |
| public string ValidInput { get; set; } | |
| public string InvalidInput { get; set; } | |
| public string InvalidMessage { get; set; } | |
| public string ValidMessage { get; set; } | |
| public string InvalidSummary { get; set; } | |
| public string ValidSummary { get; set; } | |
| } | |
| } |
Fantastic fix, thank you!
This is great, thanks! Is there any way we can control InvalidMessage / ValidMessage from the client side? I see that it ValidInput / InvalidInput works great (client and server), but InvalidMessage / ValidMessage seems to be server only and the client-side validator is still adding the 'field-validation-error' / 'field-validation-valid' classes. Thanks again!
I think you'll have to dig into the inner working of the validator then... Not sure how that could be done.
I ultimately figured it out... the values are hard-coded in jquery.validate.unobtrusive.js so a direct change is required. I added a couple of getter methods to pull the class name from the options from '$jQval.unobtrusive.options', then replaced all the hard coded values with calls to the getter functions. Fingers crossed MS does something better in .NET 8!
Thanks again!
function getFieldValidationErrorCss() {
if ($jQval.unobtrusive && $jQval.unobtrusive.options) {
if ($jQval.unobtrusive.options.fieldInvalidClass)
return $jQval.unobtrusive.options.fieldInvalidClass;
}
return "field-validation-error";
}
function getFieldValidationValidCss() {
if ($jQval.unobtrusive && $jQval.unobtrusive.options) {
if ($jQval.unobtrusive.options.fieldValidClass)
return $jQval.unobtrusive.options.fieldValidClass;
}
return "field-validation-valid";
}
Thank you!