Back to Home

Preparing ASP.NET Core: Creating Your Own Tag Helper / Microsoft Blog

asp.net core · asp.net · .net · #aspnetcolumn · tag helper · visual studio

Preparing ASP.NET Core: Creating Your Own Tag Helper

    We continue our ASP.NET Core column with a publication by Stanislav Ushakov ( JustStas ) - a team lead from DataArt. In the article, Stas talks about ways to create your own tag helpers in ASP.NET Core. Previous articles from the column can always be read at #aspnetcolumn - Vladimir Yunev
    The last time we reviewed existing in ASP.NET Core 1.0 tag helpers. This time we’ll look at creating custom tag helpers, which can simplify the generation of small reusable sections of HTML markup.


    Introduction


    Formally, a tag helper is any class that inherits from the ITagHelper interface . In practice , you don’t need to directly inherit from ITagHelper , it is more convenient to inherit from the TagHelper class by overriding the Process / ProcessAsync method, which we will discuss throughout the article.

    aspnetcolumngithubAdvice! 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).


    In order not to start with a completely empty project, select the “WebApplication” template (Figure 2), but delete the authentication: “Change Authentication”, then select “No authentication”.


    As a result, we get a project with already connected MVC, 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 name it that way, but we can say that this is an agreement, and it is better to comply with it - put all the handwritten tag helpers in this folder. We get the following project structure (Figure 3).



    EmailTagHelper


    In the Contact.cshtml view code, you can see the following markup containing email addresses.

    Support:[email protected]
    Marketing:[email protected]

    Create an email helper tag that we will use as follows:

    Support

    Generating the following HTML markup:

    [email protected]

    Such a helper tag is useful if you need to generate many links to the email addresses of one domain. Add a new file to the TagHelpers folder , 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 simplest tag helper you can see the following:
    • Helper tags adhere to the following default naming convention: the tag used in cshtml views is equal to the tag helper class name minus TagHelper . In our case, we use the email tag . This will show how to override this default behavior. You could call our tag helper and just Email , the mechanism of work would remain the same, but by the naming rule, the TagHelper postfix needs to be added.
    • Overriding the Process method , we indicate what code will be executed by our tag helper. The TagHelper class also defines a 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 helper tag we created, 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 a wildcard to add all the 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 helper tag, and not all at once, then we need to specify its full name (FQN - fully qualified name):

    @using CustomTagHelpersAndViewComponents
    @addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
    @addTagHelper "CustomTagHelpersAndViewComponents.TagHelpers.EmailTagHelper, CustomTagHelpersAndViewComponents"
    

    Now update the code for the Views / Home / Contact.cshtml view by rewriting the email links:

    Support:Support
    Marketing:Marketing

    Visual Studio 2015 remarkably highlights the help tags, our newly created tag is no exception:


    We’ll open the site in a 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 email link. We will transfer the necessary information using the email-to attribute, the email domain will be set as 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 some interesting points about this code. First point: the MailTo property from PascalCase in C # code will become
    low-kebab-case in the view code: email-to , as is customary in the front-end now. The last line of the method sets the content of the created tag. Along with this syntax, add attributes output.Attributes ["href"] = "mailto:" + address; You can use the output.Attributes.Add method .
    Now update the Views / Home / Contact.cshtml view :

    Support:
    Marketing:

    As a result, the following HTML markup will be generated:

    Support:[email protected]
    Marketing:[email protected]

    Note that we cannot write now , for this you need to add a special attribute by connecting the assembly Microsoft.AspNet.Razor.TagHelpers :

    [HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)]
    

    Also, instead of TagStructure.WithoutEndTag, you can use the value TagStructure.NormalOrSelfClosing , in which case it will be possible to write asso and .

    Helper tag bold


    We’ll write a simple helper tag that can be applied to various HTML tags. Let it turn the text into bold for all HTML tags to which it applies. We again need the HtmlTargetElement attribute . Add a folder TagHelpers new file BoldTagHelper.cs 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("");
                output.PostContent.SetHtmlContent("");
            }
        }
    }
    

    Now attribute HtmlTargetElement we do not share the tag name of the tag-helper is used, and the name of the HTML attribute that can be added to different HTML tags. Change the markup in the About.cshtml view :

    Use this area to provide additional information.


    As a result, the following HTML markup is generated:

    Use this area to provide additional information.


    However, if we try to use bold not as an attribute, but as the name of an HTML tag, the code of the tag helper will not be called. This can be fixed by adding the HtmlTargetElement attribute to the helper tag class again, only passing the bold tag name to it :

    [HtmlTargetElement("bold")]
    

    Now in the view we can write like this:

    Use this area to provide additional information.

    If we add both HtmlTargetElement attributes to our tag helper class at once:

    [HtmlTargetElement("bold")]
    [HtmlTargetElement(Attributes = "bold")]
    

    Then they will be interconnected by a logical OR: either the bold tag or the bold attribute. You can combine more than two HtmlTargetElement attributes , all of which will be linked via OR. If we want to connect them via 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 of an HTML tag. Then we must frame the entire contents of the tag with the 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 the main content and immediately after. Schematically, it looks like this:

    PRECONTENT Use this area to provide additional information. POST_CONTENT


    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

    Use this area to provide additional information.

    POST_ELEMENT

    And do not forget to use the SetHtmlContent method , not SetContent , if we want to add HTML tags, otherwise the SetContent method will encode the transmitted string and the strong tag will not apply.

    Asynchronous copyright helper tag


    Before that, we overloaded the Process method , now we will overload its asynchronous version: the ProcessAsync method . Let's write a simple helper tag that will output such markup:

    © 2016 MyCompany


    Add a new CopyrightTagHelper.cs file to the TagHelpers folder :

    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNet.Razor.TagHelpers;
    namespace CustomTagHelpersAndViewComponents.TagHelpers
    {
        /// 
        /// Custom tag helper for generating copyright information. Content inside will be added after: © Year
        /// 
        [HtmlTargetElement("copyright")]
        public class CopyrightTagHelper : TagHelper
        {
            public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
            {
                var content = await output.GetChildContentAsync();
                string copyright = $"

    © {DateTime.Now.Year} {content.GetContent()}

    "; output.Content.SetHtmlContent(copyright); } } }

    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 () construct ; . Then, using the new formatting string mechanism in C # 6, we form the content 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 helper tag, you need to know how great are written. To do this, look at the standard tag helpers available in MVC: Microsoft.AspNetCore.Mvc.TagHelpers . And we will add a sufficient number of comments and checks to our created tag helpers.

    The entire source code for the project is available at Github at: Custom Tag Helpers .

    Own tag helpers optimize common tasks for generating small HTML markups. If you want to encapsulate more complex presentation logic, it is better to use View Components .

    Try ASP.NET Core 1.0, report bugs, add features!

    For authors


    Friends, if you are interested in supporting the column with your own material, please write to me at [email protected] in order to discuss all the details. We are looking for authors who can interestingly talk about ASP.NET and other topics.


    about the author


    Stanislav Ushakov
    Senior .Net Developer / Team lead at DataArt

    Born, studied in Voronezh. .Net I've been professionally engaged for more than 6 years, before that I even saw MFC. I like to program (recently Arduino got my hands on it, play), read books (especially paper ones), play WhatWhereWhen (sports), learn and understand everything new. I finally want to protect the written candidate.

    Juststas

    Announcement! Deep Intense at DevCon 2016


    image

    We are pleased to announce the in-depth 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 5
    As part of this effort, participants will take part in an exciting and adventurous journey dedicated to developing web applications on the latest ASP.NET 5. We will go all the way from zero to a full-fledged application deployed in the cloud. And along the way, participants will be able to stop to study the internal structure of ASP.NET, work with relational and NoSQL databases using Entity Framework 7, develop applications on the MVC framework, build models, representations and business logic, create a REST API, organize continuous development processes and testing with Git and Visual Studio Online, and deploying with Azure and Docker containers. At the end of the journey, all participants will be initiated and become honored knights of ASP.NET.

    Registration for the DevCon 2016 conference is already open!Register here .

    Only registered users can participate in the survey. Please come in.

    Have you ever used your own Tag Helpers?

    • 22.4% Yes, used 20
    • 15.7% No, but it was necessary, now I know how 14
    • 43.8% Until I had to, I will know 39
    • 17.9% No, and so far I do not see the point 16

    Read Next