ASP. NET Core – Creating custom Tag Helpers

Ignite UI Team / Thursday, November 3, 2016

Asp.net Core provides a way to extend the Razor syntax with custom tags using TagHelpers.

 

Using TagHelpers makes the Razor view much easier to understand from a non C# person due to the html look. Instead of using HTML helpers for creating a label now we can use a TagHelper.
For example, if we want to create a label for our username input, we should use the following syntax.
@Html.Label("Username", "Username:", new { @class = "control-label" })
In order to set attributes to the label we should provide an anonymous object as third parameter. For a front end developer or non C# person this syntax may be unfamiliar and changing the class or adding additional class, or attribute requires a better knowledge of C#.
The same can be achieved by using the LabelTagHelper:
<ig-loader css-path="http://localhost/css"
           javascript-path="http://localhost/css"
           resources="combo"/>
The line above is much easier to understand and fits better in html, since it’s a markup declaration rather than C# code.
Because we are using TagHelpers we can take advantage of the Visual Studio IntelliSence support.
 
This looks very awesome but what about creating a custom TagHelpers, is it possible at all?
The answer is yes, this is possible and you can create complex controls such as grids and dropdowns, etc.
In order to create a custom TagHelper all we need to do is inherit the TagHelper class(Insert hyperlink here).
Let’s create TagHelper called Loader, which will load resources such as JavaScript and CSS. The Loader is an Ignite UI AMD component.
We can use the template from Visual Studio from the Add new item menu.
 
 
And that will create the following class:
using Microsoft.AspNetCore.Razor.TagHelpers;
 
namespace WebApplication1.TagHelpers
{
    // You may need to install the Microsoft.AspNetCore.Razor.Runtime package into your project
    [HtmlTargetElement("tag-name")]
    public class LoaderTagHelper : TagHelper
    {
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
 
        }
    }
}
 
Our LoaderTagHelper will have the following structure
<ig-loader css-path="http://localhost/css"
           javascript-path="http://localhost/css"
           resources="combo" />
The following will be rendered as a script tag containing html.
The TagHelper should be mapped to ig-loader by changing the value of the HtmlTargetElement to “ig-loader”. TagHelper Attributes should be mapped to C# properties, and in order to achieve this we will use the HtmlAttributeName, where we provide the name of tag helper attribute as a value.
Our class should now look like this:
using Microsoft.AspNetCore.Razor.TagHelpers;
 
namespace WebApplication1.TagHelpers
{
    [HtmlTargetElement("ig-loader")]
    public class LoaderTagHelper : TagHelper
    {
        [HtmlAttributeName("css-path")]
        public string CssPath { get; set; }
 
        [HtmlAttributeName("javascript-path")]
        public string JavaScriptPath { get; set; }
 
        [HtmlAttributeName("resources")]
        public string Resources { get; set; }
 
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
             
        }
    }
}
 
To render our TagHelper to output html we will use the Process method. We are going to set output. TagName to empty string and append the html to the output content.
 
public override void Process(TagHelperContext context, TagHelperOutput output)
{
    output.TagName = string.Empty;
    output.Content.AppendHtml($" ");
}
 
This is great, but how to create more complex TagHelpers with relation between them?
 
We can use the TagHelperContext.Items dictionary to communicate with its child TagHelper in order to create relation.
 
Let’s create a ComboTagHelper which will have a multiselection functionality.
We are giving it the following structure:
 
<ig-combo ig-data-source="@Model"
          ig-key="Id"
          ig-value="Name">
    <ig-combo-multi-selection ig-is-enabled="true" ig-enable-checkboxes="true" />
ig-combo>
We will have the following C# classes relatively:
using Microsoft.AspNetCore.Razor.TagHelpers;
 
namespace WebApplication1.TagHelpers
{
    [HtmlTargetElement("ig-combo")]
    public class ComboTagHelper : TagHelper
    {
        [HtmlAttributeName("ig-key")]
        public string Key { get; set; }
 
        [HtmlAttributeName("ig-value")]
        public string Value { get; set; }
 
        [HtmlAttributeName("ig-data-source")]
        public object DataSource { get; set; }
 
        public MultiSelectionTagHelper MultiSelection { get; set; }
 
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
 
        }
    }
}
 
and
using Microsoft.AspNetCore.Razor.TagHelpers;
 
namespace WebApplication1.TagHelpers
{
    [HtmlTargetElement("ig-combo-multi-selection")]
    public class MultiSelectionTagHelper : TagHelper
    {
        [HtmlAttributeName("ig-enable-checkboxes")]
        public bool EnableCheckBoxes { get; set; }
 
        [HtmlAttributeName("ig-is-enabled")]
        public bool Enabled { get; set; }
 
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
           
        }
    }
}
 
In order to create a relation between the TagHelpers and initialize MultiSelection property in the parent with correct context we will pass the instance of the parent in the items collection of its context.
In our parent process method, we are going to pass this to key ComboTagHelper and get child context so its attributes can be parsed.
public async override void Process(TagHelperContext context, TagHelperOutput output)
{
    context.Items.Add(typeof(ComboTagHelper), this);
    await output.GetChildContentAsync();
    // Set the output of the TagHelper
}
In 'the child process we will set a reference to MultiSelection property of the parent and suppress the output.
public override void Process(TagHelperContext context, TagHelperOutput output)
{
    ((ComboTagHelper)context.Items[typeof(ComboTagHelper)]).MultiSelectionSettings = this;
    output.SuppressOutput();
}
Now we have our child TagHelper processed as property of the parent and we can use it to render the output.
The TagHelpers are available in Volume release 16.2 of Ignite UI.