Practical .NET
The Fundamentals of Improving Productivity with Custom Tag Helpers in ASP.NET Core
Some time back I wrote a post on a custom HtmlHelper that generated my typical block of HTML: A fieldset element containing a label, a textbox and a validation message element. Since then, in ASP.NET Core, tag helpers have come along as a replacement for (or complement to) HtmlHelpers.
HtmlHelpers vs. Tag Helpers
Unlike HtmlHelpers, a tag helper is a class that attaches itself to an HTML-compliant element in a View or Razor Page. The tag helper can, through its properties, add additional attributes to the element that a developer can use to customize the tag's behavior. At compile time, the code in the tag helper's Process (or ProcessAsync) method manipulates the element the tag helper is attached to in order to create the HTML that appears in the Web page that goes to the user.
While the way you invoke tag helpers is different than HtmlHelper, fundamentally, the goal is the same: Provide a convenient way to generate HTML in a View (or Razor Page) for those developers who don't want to have to write all the HTML themselves. If you have some HTML that you can see yourself repeating in multiple pages and (a) want to make yourself more efficient or (b) want to ensure consistency in your UI across those pages, tag helpers (like HtmlHelpers) can make a lot of sense.
Tag helpers also centralize the generation of that HTML. If you should decide to change/update the HTML being generated, you don't have to touch each page -- you just rewrite your tag helper and roll it out to the Web sites that use it. And, because tag helpers are just a class, that's easy to do: You can create your tag help in a Class Library project and share the resulting class across multiple projects/sites.
During processing your code can modify both the element it's attached to and any content inside the element's open and close tags. You can add or remove attributes and even change the tag's name. You can also add HTML immediately before or after the element that your helper is attached to.
Tag helpers are especially good news for page designers. Because tag helpers attach themselves to HTML elements, page designers get to work with tags they actually understand (unlike HtmlHelpers that are, essentially, opaque to anyone with HTML and CSS knowledge). That IntelliSense support for tag helpers is better than what you get with HtmlHelpers is just icing on the cake.
Attaching to a Tag
There are two ways to attach a tag helper class to an HTML-compliant element. The most straightforward way is to give the class the same name as the tag. A tag helper must inherit from the TagHelper class, so a tag helper that attaches itself to the Label tag would look like this:
public class Label: TagHelper
{
However, you don't need to create tag helpers that attach just to existing HTML elements -- your helper will attach itself to any HTML-compliant element. For example, every one of your sites probably has a contact e-mail address on it. Odds are that the contact person varies from site to site but that the domain name (your organization) remains the same. As a result, it would be convenient to have a tag helper that would attach itself to a tag that looks like this:
<contact domain-name="phvis.com">phvogel</contact>
And then, at compile time, deliver this anchor tag to the browser:
<a href="mailto:[email protected]">Contact</a>
You can do that simply by creating a tag helper called Contact.
There are other benefits to creating tag helpers. If, for example, you change your mind and decide you want your mailto Contact element to be replaced with an anchor tag that takes the user to a page filled with contact options (e-mail, online chat, phone numers and so on) ... well, all you have to do is rewrite your tag helper to generate the new tag.
Enhancing Multiple Tags
But there's more flexibility here than just attaching your helper to specific tags. There's nothing stopping you from creating a tag helper that attaches itself to any element that meets criteria that you set. You do that by decorating your tag helper class with the HtmlTargetElement attribute. That attribute lets you specify some combination of element name, attributes and attribute values that will be used when attaching your tag helper to an element.
This code, for example, attaches itself to any element with a tag name of input and an attribute called type that's set to "email":
[HtmlTargetElement("input", attributes="[type=email]")]
public class Contact : TagHelper
Now, you can piggyback your tag helper with its domain-name attribute onto an HTML element that looks like this:
<input type="email" domain-name="phvis.com" value="peter.vogel "/>
The output would be the same: an anchor tag that replaces this input tag. I don't even have to specify a tag name -- using the HtmlTargetElement, my tag helper can extend any HTML-compliant element I want based solely on what attributes that element has.
Unfortunately, as fond as I am of tag helpers, I have to regard them as a complement to HtmlHelpers rather than a replacement. There's at least one limitation to tag helpers that makes recreating my HtmlHelper as a tag helper impossible and I'm not willing to give that HtmlHelper up. To explain why that is, I'll have to drag you through the ugly details of creating a tag helper, starting in my next column.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.