Extending TagHelpers in ASP.NET Core Applications
In this article we will demonstrate extending Tag Helpers in Asp.Net Core. This post will acquaint how with effectively expand a current label assistant for a typical utilize case. It will essentially center around the utilization of the Input Tag Helper, the [MaxLength] comment regularly found on string properties on a model, and how to get that to appropriately render the relating customer side maxlength quality on the component.
Obstacles with Some Tag Helpers:
At present, Tag helpers are to a great degree capable and make an extraordinary showing with regards to with making sense of which properties ought to and ought not be rendered for a given class. Consider the accompanying case:
[Display(Name = "John")]
[Required]
[MaxLength(8, ErrorMessage = "John cannot be longer than 8 characters.")]
public string John { get; set; }
On the off chance that you needed to utilize a label assistant, you may characterize this as takes after inside your View:
<input asp-for="John" data-val="true" />
The asp-for characteristic is seemingly a standout amongst the most widely recognized label partners that you'll experience, and its main role is to deal with restricting a particular property to the component that it embellishes.
This works fundamentally the same as the Html.TextBoxFor() aide that you ought to be usual to. It ought to peruse the traits that it can off of the property itself and after that apply the fitting characteristics. The information Val quality on the partner is just to demonstrate that we need to utilize customer side approval (e.g. jQuery Validation, and so on.) for this specific thing. So how about we see what gets rendered:
<input data-val="true"
type="text"
data-val-maxlength="John cannot be longer than 8 characters"
data-val-maxlength-max="8"
data-val-required="The John field is required."
id="John"
name="John"
value="" />
While the Tag Helpers put forth a valiant effort, it appeared to have missed the critical maxlength ascribe that we have to authorize length approval. We should perceive how we can approach stretching out it to bolster this.
Extending the Input Tag Helper
To handle this issue, we will broaden the current asp-for characteristic of the information label partner and utilize a touch of reflection to peruse the [MaxLength] explanation and render it as fundamental.
Let’s create a class named ‘MaxLengthTagHelper’ and add the following codes as shown in the code snippet below:
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace ExtendedTagHelpers
{
[HtmlTargetElement("input", Attributes = "asp-for")]
public class MaxLengthTagHelper : TagHelper
{
public override int Order { get; } = int.MaxValue;
[HtmlAttributeName("asp-for")]
public ModelExpression For { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
base.Process(context, output);
// Process only if 'maxlength' attribute is not present already
if (context.AllAttributes["maxlength"] == null)
{
// Attempt to check for a MaxLength annotation
var maxLength = GetMaxLength(For.ModelExplorer.Metadata.ValidatorMetadata);
if (maxLength > 0)
{
output.Attributes.Add("maxlength", maxLength);
}
}
}
private static int GetMaxLength(IReadOnlyList<object> validatorMetadata)
{
for (var i = 0; i < validatorMetadata.Count; i++)
{
if (validatorMetadata[i] is StringLengthAttribute stringLengthAttribute && stringLengthAttribute.MaximumLength > 0)
{
return stringLengthAttribute.MaximumLength;
}
if (validatorMetadata[i] is MaxLengthAttribute maxLengthAttribute && maxLengthAttribute.Length > 0)
{
return maxLengthAttribute.Length;
}
}
return 0;
}
}
As should be obvious inside the Process() technique, we will go in the accessible metadata for the property and emphasize through it to check whether we can discover the a current MaxLengthAttribute thing (on the off chance that one isn't as of now characterized):
// Process only if 'maxlength' attribute is not present already
if (context.AllAttributes["maxlength"] == null)
{
// Attempt to check for a MaxLength annotation
var maxLength = GetMaxLength(For.ModelExplorer.Metadata.ValidatorMetadata);
if (maxLength > 0)
{
output.Attributes.Add("maxlength", maxLength);
}
}
We could undoubtedly extend this to check the metadata for other normal information explanations and utilize a similar essential thought to include custom qualities of your own. Be that as it may, for the time being, we will proceed onward to really utilizing this specific one.
Using the Extended Tag Helper
Keeping in mind the end goal to approach this label aide inside our view, we should really import it. This should by and large be possible inside the _ViewImports.cshtml document, which is normally found in most default venture layouts and characterizes any gatherings and namespaces that the perspectives should get to.
We'll simply need to include the suitable namespace where you characterized the class and you ought to be ready:
@using YourProject
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, YourProject
Subsequent to including that, we won't have to change our code at all and we ought to see that the past code will now render as takes after:
<input data-val="true"
type="text"
data-val-maxlength="John cannot be longer than 8 characters."
data-val-maxlength-max="8"
data-val-required="The John field is required."
id="John"
name="John"
value=""
maxlength="8" />
As should be obvious, the maxlength ascribe was added to the component of course and ought to now implement a similar length limitations that are available on the model inside the program.
Extending It Further
Presently since we are as of now including the most extreme length a string can be, we will presumably need to incorporate the base also. This should be possible by the expansion of a GetMinLength() technique call and including it inside the Process() strategy to guarantee that any credits found are rendered to the component:
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// Previous code here omitted for brevity
if (context.AllAttributes["minlength"] == null)
{
// Attempt to check for a MaxLength annotation
var minLength = GetMinLength(For.ModelExplorer.Metadata.ValidatorMetadata);
if (minLength >= 0)
{
output.Attributes.Add("minlength", minLength);
}
}
}
private static int GetMinLength(IReadOnlyList<object> validatorMetadata)
{
for (var i = 0; i < validatorMetadata.Count; i++)
{
if (validatorMetadata[i] is StringLengthAttribute stringLengthAttribute && stringLengthAttribute.MinimumLength > 0)
{
return stringLengthAttribute.MaximumLength;
}
if (validatorMetadata[i] is MinLengthAttribute minLengthAttribute && minLengthAttribute.Length > 0)
{
return minLengthAttribute.Length;
}
}
return 0;
}
Presently if we somehow managed to embellish the property utilizing any mix of the [MinLength] and [MaxLength] comments:
[MinLength(2, ErrorMessage = "John must be at least 2 characters.")]
[MaxLength(8, ErrorMessage = "John cannot be longer than 8 characters.")]
public string John { get; set; }
Or the [StringLength] annotation, which can be used to set both a minimum and maximum string length size:
[StringLength(2, ErrorMessage = "John must be between 2 and 8 characters.")]
public string John { get; set; }
We should see that both the minlength and maxlength attributes are present on the rendered input element as expected:
<input data-val="true"
type="text"
data-val-maxlength="John cannot be longer than 8 characters."
data-val-maxlength-max="8"
data-val-required="The John field is required."
id="John"
name="John"
value=""
maxlength="8"
minlength="2" />