Custom formatting of validation summary and errors
Here are some extension points that you can consider to provide custom rendering for validation summary and field validation errors:
- Customize existing validation tag helpers (Register new
IHtmlGenerator
) - Create new validation tag helpers (Register new
Tag Helpers
)
Customize existing validation tag helpers
asp-validation-summary
and asp-validation-for
tag helpers use GenerateValidationSummary
and GenerateValidationMessage
methods of the registered implementation of IHtmlGenerator
service which is DefaultHtmlGenerator
by default.
You can provide your custom implementation deriving DefaultHtmlGenerator
and overriding those methods, then register the service at startup. This way those tag helpers will use your custom implementation.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddTransient<IHtmlGenerator, MyHtmlGenerator>();
}
Here is the link to source code of DefaultHtmlGenerator
to help you to customize the implementation.
Example - Creating a new implementation IHtmlGenerator
Here is just a simple example to show required namespaces and methods and simply what can goes into your custom implementation. After you provided custom implementation, don't forget to register it in ConfigureServices
like what I did above.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Html;
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.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
namespace ValidationSampleWebApplication
{
public class MyHtmlGenerator : DefaultHtmlGenerator
{
public MyHtmlGenerator(IAntiforgery antiforgery, IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, IUrlHelperFactory urlHelperFactory, HtmlEncoder htmlEncoder, ValidationHtmlAttributeProvider validationAttributeProvider)
: base(antiforgery, optionsAccessor, metadataProvider, urlHelperFactory, htmlEncoder, validationAttributeProvider)
{
}
public override TagBuilder GenerateValidationMessage(ViewContext viewContext, ModelExplorer modelExplorer, string expression, string message, string tag, object htmlAttributes)
{
return base.GenerateValidationMessage(viewContext, modelExplorer, expression, message, tag, htmlAttributes);
}
public override TagBuilder GenerateValidationSummary(ViewContext viewContext, bool excludePropertyErrors, string message, string headerTag, object htmlAttributes)
{
return base.GenerateValidationSummary(viewContext, excludePropertyErrors, message, headerTag, htmlAttributes);
}
}
}
Create new validation tag helpers
You also can author your custom tag helpers. To do so, it's enough to derive from TagHelper
and override Process
methods.
Then you can simply register created tag helpers in the view or globally in _ViewImports.cshtml
:
@using ValidationSampleWebApplication
@using ValidationSampleWebApplication.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, ValidationSampleWebApplication
Also when creating the custom tag helpers for validation you can consider:
- Creating the validation tag helper from scratch
- Drive from existing tag-helper classes
Example - Adding hasError class to a form-group div
In this example, I've created a asp-myvalidation-for
which can be applied on div
elements this way <div class="form-group" asp-myvalidation-for="LastName">
and will add hasError
class to div
if the specified field has validation error. Don't forget to register it in _ViewImports.cshtml
like what I did above.
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace ValidationSampleWebApplication
{
[HtmlTargetElement("div", Attributes = MyValidationForAttributeName)]
public class MyValidationTagHelper : TagHelper
{
private const string MyValidationForAttributeName = "asp-myvalidation-for";
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
[HtmlAttributeName(MyValidationForAttributeName)]
public ModelExpression For { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
base.Process(context, output);
ModelStateEntry entry;
ViewContext.ViewData.ModelState.TryGetValue(For.Name, out entry);
if (entry != null && entry.Errors.Count > 0)
{
var builder = new TagBuilder("div");
builder.AddCssClass("hasError");
output.MergeAttributes(builder);
}
}
}
}
Example - Adding field-validation-error
class to a form-group div
In the following example, I've added div
support to the standard asp-validation-for
tag helper. The existing tag helper just supports div element. Here I've added div
support to the asp-validation-for
tag helper and in case of error, it will add field-validation-error
otherwise, in valid cases the div
will have field-validation-valid
class.
The default behavior of the tag is in a way that it doesn't make any change in content of the tag if the tag has contents. So for adding the tag helper to an empty span
will add validation error to span, but for a div having some contents, it just changes the class of div. Don't forget to register it in _ViewImports.cshtml
like what I did above.
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.TagHelpers;
namespace ValidationSampleWebApplication
{
[HtmlTargetElement("div", Attributes = ValidationForAttributeName)]
public class MytValidationMessageTagHelper : ValidationMessageTagHelper
{
private const string ValidationForAttributeName = "asp-validation-for";
public MytValidationMessageTagHelper(IHtmlGenerator generator) : base(generator)
{
}
}
}