In the previous post we continued our exploration of the .NET Core tag library. In particular we looked at how to build a form using the library. We also saw some examples on mixing the tag library mark-up and Razor C# syntax. The tag library version of the POST form works in exactly the same way as its Razor equivalent.
The tag library is highly extensible. This implies that we can build our own custom tags. We’ll explore this with several examples in this post.
We’ll be working in our demo application DotNetCoreBookstore.
Custom tag helpers example one: a home address link
In this exercise our goal is to build a link to the home page using a custom “home” HTML element. Add a new folder called TagHelpers to the web project. This folder name is not compulsory, but it gives a good indication of its contents. Custom tag helpers are normal C# classes whose name will be the name of the custom attribute, in our example Home. It is not required but is still a good convention to attach “TagHelper” to the custom tag class name. This helps to differentiate it from other objects in the system that might have the same name.
The most straightforward way to create a tag helper is to derive from the TagHelper class in the Microsoft.AspNetCore.Razor.TagHelpers namespace and override one of its functions.
Add a C# class to the TagHelpers folder called HomeTagHelper:
The tag doesn’t do anything yet. We first want to register the tag helper in the Views/_ViewImports.cshtml file. We’ll use the same ‘*’ notation to tell MVC to register all tag helpers it can find in our assembly, i.e. DotNetCodeBookstore:
This will ensure that all our future tag helpers that are derived from the TagHelper class will be automatically picked up by MVC.
Rebuild the solution and open Books/Index.cshtml. Start typing ‘<ho' right above the FunnyMessage section. If the tag helper was registered correctly, then you should see the "home" element in the list of available elements by IntelliSense:
We’ll just complete the element to begin with:
Set a breakpoint in the overridden Process function, start the application and navigate to /books. Code execution will stop at the breakpoint. This confirms that MVC has correctly found our custom tag and wants to execute its Process method.
Let the code execute and view the source of the index page. Since we haven’t told MVC how to render the home attribute it will simply send…
…to the client, exactly as we declared it in the markup.
We can now add some body to the Process method:
The first line sets the element name to an anchor, i.e. “a”. Then we set the home URL extension to the href attribute of the anchor. The SetContent function defines the text content of the tag, i.e. the text that will be rendered between the opening and closing “a” tags.
Run the application and you’ll see that the home tag was rendered as a link on the index page:
The HTML source also looks good:
Example two: adding attributes to the custom home tag
We’ll now see how to add custom attributes to our tag. We’ll add an attribute to set the address extension and the text content. Attributes are provided through properties of the implemented tag helper:
The pascal case properties are exposed as kebab-case attributes in the markup. BookstoreHomeAddress will become bookstore-home-address and BookstoreHomeLinkContent will show up as bookstore-home-link-content. This also gives a hint as how the built-in “asp-” attributes were added. Their property names all start with Asp. Rebuild the project and see what IntelliSense comes up with:
That kind of prefix hint looks familiar from the “asp-” attributes we saw before. Extend the markup in Index.cshtml:
The custom attribute was rendered correctly again:
Exercise three: asynchronous tag helper
The TagHelper class has a ProcessAsync function that transforms the custom tag asynchronously. Add a new tag helper to the TagHelpers folder called HomeAsyncTagHelper:
Most of the code is the same as before but we have a couple of new elements:
ProcessAsync returns a Task which is customary for asynchronous functions in C#
TagHelperContent is extracted from the TagHelperOutput asynchronously
TagHelperContent has a GetContent function which reads the text content in between the opening and closing tags
We set the tag content into a new attribute called “greeting”
Let’s see how this works. Add the following markup to Index.cshtml:
The text content of the home-async tags, i.e. “Hello world” will be extracted by the TagHelperContent.GetContent() method.
If you run the application now and go to /books then you’ll see the new link tag rendered as follows:
Exercise four: reverse tag and attribute
In this exercise we’ll try something moderately funny. The text content of a custom tag called “reverse” will be reversed as the name suggests. The tag will also function as an attribute for other text-related elements like p or h1.
Add a new class called ReverseTextTagHelper to the TagHelpers folder:
The HtmlTargetElement attribute is applied twice which results in an OR operation. The tag name must be “reverse” or the attribute must be “reverse” in order for .NET Core to find a match and run the overridden ProcessAsync method. In the method body we extract the text content of the element, reverse it and put the result back as the final content. First, however, we remove the attribute from the markup so that it’s not shown for the client. Not that it hurts anyone to make it visible so it’s really just cosmetics. The attribute doesn’t need to be removed from the markup to make it work.
Here’s the updated books/index.cshtml file with two examples: one where “reverse” is used as an attribute and another where it’s used as an element. Here I only show the relevant changes:
Run the application and go to the books page. You’ll see that the authors’ names have been reversed like “htimS nhoJ” and “repooC werdnA”. The Hello World message was also reversed: “dlrow olleH”.
We’ll continue with more examples in the next post.
View the list of MVC and Web API related posts here.