ASP.NET Core extending built-in tag helpers

ASP.NET Core: extending built-in tag helpers

In this post I’ll show you how you can extend built-in tag helpers.

What are tag helpers?

Basically tag helpers are classes which take part in processing (or creating) HTML elements inside Razor views. You can create C# code which will target desired HTML and produce desired output. For more throughtout explanation see Introduction to Tag Helpers in ASP.NET Core

Setup

Before we begin, make sure that your setup is correct. If your custom tag helpers are in Solution assembly then you need to add this line to your View/_ViewImports.cshtml file:

@addTagHelper *, Solution

How to add a HTML class attribute to all img elements by extending ImageTagHelper.

If you use Bootstrap you may want to add img-fluid css class to images to automatically make all available images responsive.

We can achieve this by extending ImageTagHelper. Notice that we’re merging attributes, so our tag helper won’t override classes which are already present in the markup.

    
    [HtmlTargetElement("img", TagStructure = TagStructure.WithoutEndTag)]
    public class ResponsiveImageTagHelper : ImageTagHelper
    {
        public ResponsiveImageTagHelper(IHostingEnvironment hostingEnvironment, IMemoryCache cache, HtmlEncoder htmlEncoder, IUrlHelperFactory urlHelperFactory)
            : base(hostingEnvironment, cache, htmlEncoder, urlHelperFactory)
        {
        }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            base.Process(context, output);

            var img = new TagBuilder("img");
            img.Attributes.Add("class", "img-fluid");
            output.MergeAttributes(img);
        }
    }

Results:

// input
<img class="border" src="image.png" />
// output
<img class="border img-fluid" src="image.png" />

How to add required indicator to labels which refer to [Required] properties by extending LabelTagHelper.

I’ll show you a way to add “(required)” text to all labels which point to properties with Required attribute.

    [HtmlTargetElement("label", Attributes = "asp-for")]
    public class LabelRequiredTagHelper : LabelTagHelper
    {
        public LabelRequiredTagHelper(IHtmlGenerator htmlGenerator)
            : base(htmlGenerator)
        {
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            await base.ProcessAsync(context, output);

            var metadata = For.Metadata as DefaultModelMetadata;
            bool hasRequiredAttribute = metadata
                                        ?.Attributes
                                        .PropertyAttributes
                                        .Any(i => i.GetType() == typeof(RequiredAttribute)) ?? false;
            if (hasRequiredAttribute)
            {
                output.PostContent.AppendHtml("(required)");
            }
        }
    }

Results:

// input
<label asp-for="Username"></label>
// output
<label for="Username">Username<span>(required)</span></label>

How to show number of allowed characters next to textarea by extending TextAreaTagHelper.

Textarea has maxlength attribute, we’re going to display a message which’ll print it out with additional message. Note that we should use MaxLength attribute (from DataAnnotations) to check maximum length of sent data. I’ve ommited this for brevity.

    [HtmlTargetElement("textarea", Attributes = "asp-for")]
    public class ExtendedTextareaTagHelper : TextAreaTagHelper
    {
        public ExtendedTextareaTagHelper(IHtmlGenerator generator)
            : base(generator)
        {
        }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            base.Process(context, output);

            if (!output.Attributes.TryGetAttribute("maxlength", out TagHelperAttribute maxLengthAttribute))
            {
                return;
            }

            var description = $"Only {maxLengthAttribute.Value} characters allowed!";
            output.PostElement.AppendHtml(description);
        }
    }

Results:

// input
<textarea asp-for="Description" maxlength="1024"></textarea>
// output
<textarea asp-for="Description" maxlength="1024"></textarea><p>Only <b>1024</b> characters allowed!</p>

BONUS: Did you know that you can add TagHelper to any HTML element?

One of the great things about tag helpers is that you can make it work for any existing HTML tags. I’ve show you examples of extending existing tag helpers, but you can also create new tag helpers for any element.

As an example, you could create tag helper which would create sortable links for all table header cells (something linke <th asp-for="FirstName"></th>). Another example could be printout out “Empty” inside a table with no rows.