We continue our column on the topic of ASP.NET Core publication from Stanislav Ushakov ( JustStas ) - team lead from DataArt. In the article, Stas talks about how to create your own tag-helpers in ASP.NET Core. Previous articles from the column can always be read on the link #aspnetcolumn - Vladimir Yunev
Last time, we looked at tag-helpers that already exist in ASP.NET Core 1.0. This time we will consider creating your own tag-helper, which can simplify the generation of small reusable sections of HTML markup.
Introduction
Formally, the tag-helper is any class that inherits from the
ITagHelper interface. In practice, it is not necessary to inherit directly from
ITagHelper , it is more convenient to inherit from the
TagHelper class, overriding the Process / ProcessAsync method that we will look at in the course of the article.
Tip! You can try everything yourself or by downloading the source code from GitHub https://github.com/StanislavUshakov/CustomTagHelpers .
Infrastructure
We will use Visual Studio 2015 Community Edition. Create a new ASP.NET 5 site (Figure 1).
Not to start with a completely empty project, select the “WebApplication” template (Figure 2), but remove the authentication: “Change Authentication”, then select “No authentication”.
')
As a result, we will get a project with MVC already connected, one controller and three actions: Index, About, Contact. Add a new folder to the project, let's call it
TagHelpers : it is not necessary to call it that way, but you can say that this is an agreement, and it is better to follow it - put all your own written tag-helper in this folder. We obtain the following structure of the project (Figure 3).
EmailTagHelper
In the view code of
Contact.cshtml, you can see the following markup containing email addresses.
<address> <strong>Support:</strong><a href="mailto:Support@example.com">Support@example.com</a><br /> <strong>Marketing:</strong><a href="mailto:Marketing@example.com">Marketing@example.com</a> </address>
Create a tag helper
email , which will be used as follows:
<email>Support</email>
Generating the following HTML markup:
<a href="mailto:Support@example.com ">Support@example.com</a>
Such a tag-helper is useful if you need to generate a lot of links to email addresses of one domain. Add a new file to the
TagHelpers folder, let's call it
EmailTagHelper.cs .
usingMicrosoft.AspNet.Razor.TagHelpers; namespaceCustomTagHelpersAndViewComponents.TagHelpers { public class EmailTagHelper : TagHelper { public string MailTo { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "a";
Already on the example of this very simple tag-helper one can see the following:
- Tag-helper follow the following default naming rule: a tag used in cshtml views is equal to the name of the tag-helper class minus TagHelper . In our case, use the email tag. Further it will be shown how to override this default behavior. It would be possible to call our tag-helper and just Email , the mechanism of operation would remain the same, but according to the naming rule, the postfix TagHelper needs to be added.
- Overriding the Process method, we specify which code will be executed by our tag-helper. Also, the TagHelper class defines the ProcessAsync method with the same parameters.
- The context parameter contains information about the execution context of the current HTML tag.
- The output parameter stores data about the original HTML element that was used in the view and its contents.
In order to use the tag-helper created by us, we will add a directive to the
Views / _ViewImports.cshtml file to connect it.
@using CustomTagHelpersAndViewComponents @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" @addTagHelper "*, CustomTagHelpersAndViewComponents"
We use the wildcard character to add all tag-helpers from the assembly to the scope at once. The first argument to the
@addTagHelper directive is the name of the tag-helper (or asterisk), the second is the assembly in which they are searched. If we want to add one tag-helper, but not all at once, then we must indicate its full name (FQN - fully qualified name):
@using CustomTagHelpersAndViewComponents @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" @addTagHelper "CustomTagHelpersAndViewComponents.TagHelpers.EmailTagHelper, CustomTagHelpersAndViewComponents"
Now update the
Views / Home / Contact.cshtml view code by rewriting the email links:
<address> <strong>Support:</strong><email>Support</email><br /> <strong>Marketing:</strong><email>Marketing</email> </address>
Visual Studio 2015 perfectly highlights the tag-helpers, our newly created one is not an exception:
Open the site in the browser and see that the
email tag has been replaced with the
a tag, it's time to add the necessary logic to generate the correct link to the email. We will transmit the necessary information using the
email-to attribute, the
email domain will be set by a constant in the tag-helper.
using Microsoft.AspNet.Razor.TagHelpers; namespace CustomTagHelpersAndViewComponents.TagHelpers { public class EmailTagHelper : TagHelper { private const string EmailDomain = "example.com"; public string MailTo { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { output.TagName = "a"; string address = MailTo + "@" + EmailDomain; output.Attributes["href"] = "mailto:" + address; output.Content.SetContent(address); } } }
Note the interesting points of this code. The first point: the
MailTo property from PascalCase in C # code will become
low-kebab-case in the presentation code:
email-to , as is now accepted in front-end. The last line of the method sets the contents of the created tag. Along with the syntax for adding attributes
output.Attributes ["href"] = "mailto:" + address; You can use the
output.Attributes.Add method.
Now update the
Views / Home / Contact.cshtml view :
<address> <strong>Support:</strong> <email mail-to="Support"></email><br /> <strong>Marketing:</strong> <email mail-to="Marketing"></email> </address>
As a result, the following HTML markup will be generated:
<address> <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br> <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a> </address>
Note that now we cannot write
<email mail-to = "Support" /> , for this we need to add a special attribute by connecting the
Microsoft.AspNet.Razor.TagHelpers assembly:
[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)]
Also, instead of
TagStructure.WithoutEndTag, you can use the
TagStructure.NormalOrSelfClosing value, in this case, you can write both
<email> </ email> and
<email /> .
Tag-helper bold
Let's write a simple tag helper that can be applied to various HTML tags. Let it turns the text into bold for all HTML tags to which it applies. We will again need the
HtmlTargetElement attribute. Add a new
BoldTagHelper.cs file to the
TagHelpers folder with the following contents:
using Microsoft.AspNet.Razor.TagHelpers; namespace CustomTagHelpersAndViewComponents.TagHelpers { [HtmlTargetElement(Attributes = "bold")] public class BoldTagHelper : TagHelper { public override void Process(TagHelperContext context, TagHelperOutput output) { output.Attributes.RemoveAll("bold"); output.PreContent.SetHtmlContent("<strong>"); output.PostContent.SetHtmlContent("</strong>"); } } }
Now in the
HtmlTargetElement attribute we do not pass the name of the tag to which the helper tag is applied, but the name of the HTML attribute that can be added to different HTML tags. Change the markup in the
About.cshtml view :
<p bold>Use this area to provide additional information.</p>
As a result, the following HTML markup will be generated:
<p><strong>Use this area to provide additional information.</strong></p>
However, if we try to use
bold not as an attribute, but as the name of an HTML tag, the tag-helper code will not be called. This can be corrected by adding the HtmlTargetElement attribute to the tag-helper
class again, only passing it the name of the
bold tag:
[HtmlTargetElement("bold")]
Now in the submission we can write like this:
<bold>Use this area to provide additional information.</bold >
If we immediately add both
HtmlTargetElement attributes to our tag-helper class:
[HtmlTargetElement("bold")] [HtmlTargetElement(Attributes = "bold")]
Then they will be linked with each other by logical OR: either the bold tag or the bold attribute. You can combine more than two attributes
HtmlTargetElement , they will all be linked through OR. If we want to link them through an AND: apply to the bold tag with the bold attribute, we need to combine these conditions in one
HtmlTargetElement attribute:
[HtmlTargetElement("bold", Attributes = "bold")]
Now consider the code inside the
Process method. Method
output.Attributes.RemoveAll ("bold"); removes the
bold attribute from the HTML tag. Then we need to frame the entire contents of the tag with a
strong tag. To do this, use the
PreContent and
PostContent properties of the
output object. They allow you to add new content inside the tag, but before and immediately after the main content. Schematically, it looks like this:
<p bold>PRECONTENT Use this area to provide additional information. POST_CONTENT</p>
If we want to add new content outside the tag, we need to use the
PreElement and
PostElement properties , then we get the following:
PRE_ELEMENT<p bold>Use this area to provide additional information.</p>POST_ELEMENT
And do not forget to use the
SetHtmlContent method, not
SetContent , if we want to add HTML tags, otherwise the
SetContent method recodes the transferred string and the
strong tag will not be applied.
Asynchronous tag-helper copyright
Before we overloaded the
Process method, now we will overload its asynchronous version: the
ProcessAsync method. Let's write a simple tag helper that will display this markup:
<p>© 2016 MyCompany</p>
Add a new file
CopyrightTagHelper.cs to the
TagHelpers folder:
using System; using System.Threading.Tasks; using Microsoft.AspNet.Razor.TagHelpers; namespace CustomTagHelpersAndViewComponents.TagHelpers {
Unlike the synchronous
Process method, the
ProcessAsync method returns an object of type
Task . We also added the
async modifier to the method, because inside the method we get the contents of the tag using the
await output.GetChildContentAsync (); . Then, using the new formatting string mechanism in C # 6, we
create the contents of the tag and add it using the
output.Content.SetHtmlContent (copyright) method
; . In all other aspects, this simple asynchronous helper tag is no different from synchronous.
Best practices and conclusions
To write a good tag helper, you need to know how to write excellent. To do this, look at the standard tag-helpers available in MVC:
Microsoft.AspNetCore.Mvc.TagHelpers . And add in your created tag-helper enough comments and checks.
All source code of the project is available in GitHab at:
Custom Tag Helpers .
Own tag-helpers allow you to optimize frequently encountered tasks for generating small HTML markup. If you want to encapsulate the more complex presentation logic, it’s better to use
View Components .
Try ASP.NET Core 1.0, report bugs, add features!
To authors
Friends, if you are interested in supporting the column with your own material, please write to me at
vyunev@microsoft.com to discuss all the details. We are looking for authors who can interestingly tell about ASP.NET and other topics.

about the author
Stanislav UshakovSenior .Net Developer / Team lead in DataArt
Born, studied in Voronezh. .Net has been professionally engaged for more than 6 years, before that even MFC had seen. I like to program (recently I got my hands on Arduino, I play), read books (especially paper ones), play WhatSheKogda (sports), learn and understand everything new. I want to finally defend the written candidate.
JuststasAnnouncement! Deep Intense at the DevCon 2016 Conference

We are pleased to announce the holding of a deep intensive course on ASP.NET Core at the [DevCon 2016 conference] (https://events.techdays.ru/DevCon/2016/registration). This course will take place on the second day of the conference and will take a full day of the conference for 6 hours.
Development of modern web applications on the open platform ASP.NET 5As part of this intense, participants will take part in an exciting and adventurous journey dedicated to developing web applications on the latest ASP.NET platform 5. We will go all the way from zero to a full-fledged application deployed in the cloud. And on the way, participants will be able to stop to explore the internal structure of ASP.NET, work with relational and NoSQL databases using the Entity Framework 7, develop applications on the MVC framework, build models, views and business logic, create a REST API, and organize continuous development processes. and testing using Git and Visual Studio Online, as well as deploying using Azure and Docker containers. At the end of the trip, all participants will pass the dedication and become deserved knights of ASP.NET.
Registration for the DevCon 2016 conference is already open!
Sign up here .