Custom formatting of summary and validation errors

Problem

Summary

For a validation summary, you usually have something like this:

<div asp-validation-summary="ModelOnly" class="..."></div>

which in case of errors with an empty string, because the field / attribute will be displayed inside this div inside the list <ul>.

What if I want to display them using a sequence divwith a specific attribute class? Or any other custom formatting?

Field check

To check the fields you usually do:

<div class="form-group">
    <label asp-for="OldPassword"></label>
    <input asp-for="OldPassword" class="form-control" />
    <span asp-validation-for="OldPassword" class="text-danger"></span>
</div>

and the error is inserted as text inside the element span.

I use a template that requires the class to be has-errorsapplied to the element form-group divin case of errors, because it needs to style both the label and the input.

, span div ( ) , , span div div ; , span a div , div , , , div .

(tl; dr)

( DRY, ) , ?

+8
2

, :

  • ( IHtmlGenerator)
  • ( Tag Helpers)

asp-validation-summary asp-validation-for GenerateValidationSummary GenerateValidationMessage IHtmlGenerator, DefaultHtmlGenerator.

, DefaultHtmlGenerator , . , .

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddTransient<IHtmlGenerator, MyHtmlGenerator>();
}

DefaultHtmlGenerator, .

- IHtmlGenerator

, , . , , ConfigureServices, .

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

. TagHelper Process.

_ViewImports.cshtml:

@using ValidationSampleWebApplication
@using ValidationSampleWebApplication.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, ValidationSampleWebApplication

:

- hasError div

asp-myvalidation-for, div <div class="form-group" asp-myvalidation-for="LastName"> hasError div, . _ViewImports.cshtml, .

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

- field-validation-error div

div asp-validation-for. div. div asp-validation-for, field-validation-error, , div field-validation-valid.

, , . , helper span span, div, , div. _ViewImports.cshtml, .

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)
        {
        }
    }
}
+9

, :

[HtmlTargetElement("ul", Attributes = AttributeName)]
public class ValidationSummaryLiItemsTagHelper : TagHelper
{
    private const string AttributeName = "model-validation-summary-list";

    [HtmlAttributeNotBound]
    [ViewContext]
    public ViewContext ViewContext { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        var errors = ViewContext.ModelState.Where(x => x.Key == "").SelectMany(x => x.Value.Errors).ToList();
        foreach (var error in errors)
        {
            output.Content.AppendFormat("<li>{0}</li>", error.ErrorMessage);
        }

        if (errors.Any() == false)
            output.SuppressOutput();
    }
}

:

<ul class="some-css-class" id="some-id-name" model-validation-summary-list></ul>

, , ul html.

0

All Articles