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!!