January 2018 : Instructor-led Online Course in ASP.NET Core 2.0. Conducted by Bipin Joshi. Read more...
Registration for January 2018 batch of ASP.NET Core 2.0 instructor-led online course has already started. Conducted by Bipin Joshi. Register today ! Click here for more details.

Create Custom Tag Helpers in ASP.NET Core

As an ASP.NET developer you are already familiar with server controls and HTML helpers. What do they do? Simply put they execute some server side logic and generate HTML markup. The HTML markup thus generated is then rendered in the browser. In ASP.NET core you can continue to use HTML helpers as in ASP.NET MVC. However, there is a better alternative - Tag Helpers.

ASP.NET Core tag helpers consist of markup they you place in your views. This markup is special in that it is processed on the server side and renders certain HTML markup. Just to get an idea consider the following Form tag helper:

<form asp-action="ProcessForm" asp-controller="Home" method="post">
....
....
</form>

The above markup indices that you are using a form tag helper. Notice that the form tag helper has two attributes of the form asp-*. These attributes indicate the action name and the controller name respectively. There is also a method attribute. The asp-action and asp-controller attributes are processed on the server whereas the method attribute is the standard HTML attribute. The above server side markup gets converted to this :

<form action="/home/ProcessForm" method="post>
....
....
</form>

Tag helpers are better than HTML helpers because they are markup friendly. They seem more natural to go along standard HTML markup. What's more is you can easily build your own custom tag helpers. The remainder of this article will illustrate how.

The Latestblogposts tag helper - markup

A custom tag helper has two parts - a piece of markup that you place in a razor view and a class that houses the processing logic of the tag helper under consideration. Let's understand this with an example. Suppose you are building a blog engine and wish to display a list of latest blog posts belonging to a particular category. You also want to control the number of items being displayed. Considering these requirements let's say we wish to have this kind of custom tag helper markup in our view:

<latestblogposts Category="AspNetCore" Count="10">

</latestblogposts>

Here, the latestblogpost tag helper presents itself as an independent markup element. It has two attributes namely Category and Count. The Category attribute indicates the blog post category or tag to be used (AspNetCore in this case) to pick the latest posts. The Count attribute indicates that 10 items are to be displayed in the latest posts list.

The Latestblogpost tag helper - class

This is the first part of our custom tag helper. Now we need to create the remaining part a class named LatestblogpostsTagHelper. This class is shown below:

public class LatestblogpostsTagHelper:TagHelper
{
    public string Category { get; set; }
    public int Count { get; set; }

    public override void Process(TagHelperContext context, 
                         TagHelperOutput output)
    {
        output.TagName = "div";
        output.Attributes.Add("class", "latestBlogPosts");

        using (BlogDbContext db = new BlogDbContext())
        {
            var query = (from p in db.BlogPosts
                            where p.Category == Category
                            orderby p.PublishDate descending
                            select p
                            ).Take(Count);

            StringBuilder sb = new StringBuilder();
            foreach(var item in query)
            {
                sb.AppendFormat("<h2><a href='/home/
                displaypost/{0}'>{1}</a></h2>",item.BlogPostID,
                item.Title);
            }
            output.Content.SetHtmlContent(sb.ToString());
        }
    }
}

Notice the above code carefully. The LatestblogpostsTagHelper class inherits from TagHelper class (Microsoft.AspNetCore.Razor.TagHelpers namespace). The LatestblogpostsTagHelper class consists of two properties - Category and Count - and an overridden Process() method.

The Category and Count properties correspond to the respective attributes used in the tag helper markup earlier. The Process() method is where the processing logic of the custom tag helper resides. The Process() method receives TagHelperOutput parameter that is used to render the desired output. The TagName property indicates the HTML tag name of the resultant markup (div in this case). The Attributes collection is used to set the class  attribute of the <div> to latestBlogPosts. Then an Entity Framework Core code connects with the database and fetches records from the BlogPosts table. The BlogDbContext and BlogPost are discussed later.

Notice how the LINQ to Entities query makes use of the Category and Count properties while retrieving the records. A foreach loop then iterates through the latest blog posts and generates a list of hyperlinks for each post. This is done using a StringBuilder. Finally, SetHtmlContent() method is used to render the HTML fragment.

DbContext and entity class

This completes the Latestblogposts tag helper. Before running the application you also need to add the DbContext and the BlogPost entity class. They are shown below:

[Table("BlogPosts")]
public class BlogPost
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int BlogPostID { get; set; }
    [Required]
    [StringLength(200)]
    public string Title { get; set; }
    [Required]
    public string Content { get; set; }
    [Required]
    [StringLength(50)]
    public string Category { get; set; }
    [Required]
    public DateTime PublishDate { get; set; }
}
public class BlogDbContext:DbContext
{
    public DbSet<BlogPost> BlogPosts { get; set; }

    protected override void OnConfiguring
         (DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"data source=.; 
           initial catalog =BlogDb; 
           Integrated Security=True; 
           MultipleActiveResultSets = true");
    }
}

To create a database based on the above DbContext you can use the following commands:

> dotnet ef migrations add MyMigrations
> dotnet ef database update

Of course, you can also create the database manually instead of using these commands.

Using the custom tag helper

In order to use the latestblogposts tag helper successfully on a razor view, it must be made available to the view. You do this using the @addtagHelper directive. You can place this directive either inside the view file itself or better yet inside _ViewImports.cshtml file.

@addTagHelper *,MyWebApplication

The @addTagHelper directive specifies that all the tag helpers (*) from the MyWebApplication assembly (the current web application's project) are to be made available to the view. You will also get IntelliSense for the custom tag helpers:

After creating the database and adding a few dummy records run the application. The following figure shows a sample run of the application.

If you see the HTML source of the page, you will notice this HTML fragment:

<div class="latestBlogPosts">
  <h2>
    <a href='/home/displaypost/4'>Blog Post Title 2</a>
  </h2>
  <h2>
    <a href='/home/displaypost/1'>Blog Post Title 1</a>
  </h2>
</div>

Tag helpers and Pascal casing

In the above example our tag helper element was <latestblogposts>. What if you wish to use the following tag name:

<Latest-Blog-Posts Category="AspNetCore" Count="10">
</Latest-Blog-Posts>

Here the tag name includes hyphen character. You can't use hyphen in C# class names. The remedy is to follow Pascal casing for the tag helper class name. So, our class name needs to be :

public class LatestBlogPostsTagHelper : TagHelper
{
  ....
}

The same rule applies for attribute names also. Thus article-category attribute would be mapped with ArticleCategory attribute and article-count attribute will correspond to ArticleCount attribute.

Specifying element and attribute mapping manually

In the preceding example the markup element <latestblogposts> was mapped to LatestblogpostsTagHelper class automatically. On the same lines Category and Count attributes were mapped to Category and Count properties automatically. Although this may work fine in many situations, at times you may want to explicitly specify this mapping. You can do so as shown below:

[HtmlTargetElement("latestblogposts")]
public class LatestblogpostsTagHelper:TagHelper
{
    [HtmlAttributeName("Category")]
    public string Category { get; set; }

    [HtmlAttributeName("Count")]
    public int Count { get; set; }
    ....
    ....
}

As you can see the LatestblogpostsTagHelper class is decorated with [HtmlTargetElement] attribute that maps it to the latestblogposts element. Similarly, Category and Count properties are decorated with [HtmlAttributeName] attribute. The [HtmlAttributeName] attribute maps the underlying properties to Category and Count attributes respectively.

Creating tag helpers for standard HTML elements

In the preceding example we created a new markup element - <latestblogposts> - to represent a custom tag helper. What if you wish to use a standard HTML element instead of introducing your own? Say for example :

<div article-category="AspNetCore" article-count="10">

</div>

In this case the custom tag helper takes a form of <div> element. The <div> element has two attributes article-category and article-count.

The class that handles the above tag helper is shown below:

[HtmlTargetElement("div",Attributes = "article-*")]
public class LatestBlogPostsTagHelper : TagHelper
{
    public string ArticleCategory { get; set; }
    public int ArticleCount { get; set; }

    public override void Process(
           TagHelperContext context, 
           TagHelperOutput output)
    {
        output.Attributes.Add("class", "latestBlogPosts");
        using (BlogDbContext db = new BlogDbContext())
        {
            var query = (from p in db.BlogPosts
                            where p.Category == ArticleCategory
                            orderby p.PublishDate descending
                            select p
                            ).Take(ArticleCount);

            StringBuilder sb = new StringBuilder();
            foreach (var item in query)
            {
                sb.AppendFormat("<h2><a href='/home/
                   displaypost/{0}'>{1}</a></h2>", 
                   item.BlogPostID, item.Title);
            }
            output.Content.SetHtmlContent(sb.ToString());
        }
    }
}

Notice that the LatestBlogPostsTagHelper class is now decorated with [HtmlTargetElement] attribute. The target element is set to div. Now, there could be many <div> elements on the page. We wish to invoke our helper only for a <div> that has article-category and article-count attributes (you could have used any other attribute names). The second parameter sets the Attributes filter to article-*. This way only the <div> elements having attributes of the form article-* are processed by this helper.

That's it! Keep coding!!


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Ajapa Yoga to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 11 July 2016


Tags : ASP.NET ASP.NET Core MVC C# Visual Studio