📜 ⬆️ ⬇️

Preparing an ASP.NET Core: Creating Your Own Tag Helper

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.

aspnetcolumngithub 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"; //   <email>   <a> } } } 

Already on the example of this very simple tag-helper one can see the following:

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 { /// <summary> /// Custom tag helper for generating copyright information. Content inside will be added after: © Year /// </summary> [HtmlTargetElement("copyright")] public class CopyrightTagHelper : TagHelper { public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { var content = await output.GetChildContentAsync(); string copyright = $"<p>© {DateTime.Now.Year} {content.GetContent()}</p>"; 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 (); . 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 Ushakov
Senior .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.

Juststas

Announcement! Deep Intense at the DevCon 2016 Conference


image

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 5
As 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 .

Source: https://habr.com/ru/post/278251/


All Articles