Skip to content

Instantly share code, notes, and snippets.

@FWest98
Last active November 16, 2022 14:00
Show Gist options
  • Save FWest98/9141b3c2f260bee0e46058897d2017d2 to your computer and use it in GitHub Desktop.
Save FWest98/9141b3c2f260bee0e46058897d2017d2 to your computer and use it in GitHub Desktop.
.NET Core 3 Custom Validation Classnames
// 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; }
}
}
@b6025
Copy link

b6025 commented Nov 16, 2022

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";
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment