-
-
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; } | |
} | |
} |
Thank you!
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";
}
I am quite sure it still works on .NET 6, but I developed it for .NET Core 3.1 and then used it for .NET 5. I indeed use bootstrap (but an older version I believe) with the following part in my
appsettings.json
: