ASP.NET Core 1.0: Goodbye HTML helpers and hello TagHelpers!

Synopsis: ASP.NET Core 1.0 [MVC 6] comes with a new exciting feature called TagHelpers. Read on to see why I think we can kiss HTML helpers goodbye. Find the accompanying sourcecode on my GitHub [4]. Please note that MVC 6 will be mentioned in square brackets, because at the moment of writing this it was still called that way, but it is all simply called ASP.Net Core 1.0 now. I first wrote this article for SDN magazine #128[8].

What are TagHelpers?

TagHelpers can be seen as the evolution of HTML helpers which were introduced with the launch of the first MVC framework. To provide context you have to imagine that with classic ASP the only way you could automate the generation of HTML is via custom subroutines. After that ASP.NET came with server controls, with view states as biggest plus, to simulate the look and feel of desktop applications and help with the transition for desktop developers. But we all know what happens when we try to jam squares in to round holes. We had to face the fact that web development is nothing like desktop development. To get in line with proper web development the ASP.NET MVC framework was launched with HTML helpers to automate the HTML output. But HTML helpers never really gelled, especially not with front end developers and designers. One of the main pet peeves was that it made you switch a lot from angle brackets (HTML, CSS) to C# (Razor syntax) during work on views, which made the experience unnecessarily uncomfortable. [MVC 6] wants to address this and some smaller issues by introducing TagHelpers. More on this here [1]. On the view side they totally behave like HTML tags. And this reduces context switching. The easiest way to introduce a TagHelper is to look at the one for the anchor tag. With the HTML helper this would’ve been done as:

@Html.ActionLink(”Home”, ”Index”, ”Home”)

With the anchor TagHelper this would look like:

taghelpers_2
Side note: Please note that asp- is just a convention, but more on that later.

The output rendered in the browser is the same for both:

taghelpers_2
Side note: Provided the default route has not been altered.

This should illustrate that a designer for instance is much better acquainted with the TagHelper syntax as it handles just like plain HTML, as opposed to the HTML helper which is almost like calling a method in C#.

The hyperlink TagHelper is not the only one provided by [MVC 6]. Let’s have a quick run through and start off by how to actually get the TagHelpers in your project.

Common TagHelpers

[MVC 6] comes with a bag of TagHelpers and to add them to your project you need to add the following line to the dependencies section of the project.json file:

”Microsoft.AspNet.Mvc.TagHelpers”: ”6.0.0-rc1-final”

Side note:When you save the file Visual Studio should automatically download and install the appropriate NuGet package(s), but if it doesn’t then you can also right click on the project and choose “restore packages” from the context menu or use the command “dnu restore” in the developer’s command prompt from the folder where the solution file resides.

Then you need to make the view aware that you would like to enable TagHelpers. This is an explicit directive, which is the way you want it! Just add this line of code to the top of your view:

@addTagHelper ”*, Microsoft.AspNet.Mvc.TagHelpers”

Notice how the directive makes use of the glob notation [2]. The first parameter is the name (or names) of the TagHelper(s) you want to use. By specifying the wildcard (*) you instruct the framework to add all TagHelpers. The second parameter is the assembly name. So here we give the instruction to add all TagHelpers from the Microsoft.AspNet.Mvc.TagHelpers assembly.

Side note: You could also add this directive to a new Razor file introduced with ASP.NET 5 called _ViewImports.cshtml. Its main purpose is to provide namespaces which all other views can use. Similar to what the web.config file in the Views folder used to do. You can add this file to any view folder as to have finer granular control over which views have access to what exactly. And it’s additive, pretty convenient! You can read more on this file here [6].

Once this line is added to your view you’ll have access to the following predefined TagHelpers:

  • Anchor
  • Cache
  • Environment
  • Form
  • Input
  • Label
  • Link
  • Option
  • Script
  • Select
  • TextArea
  • ValidationMessage
  • ValidationSummary

Side note: If you don’t see TagHelpers in your IntelliSense you need to add a reference to “Microsoft.AspNet.Tooling.Razor”: “1.0.0-rc1-final” in your project.json file.

Let’s briefly run through these Tag Helpers.

Anchor

Well we’ve seen this at its simplest form already:

taghelpers_2

Just the “asp-controller” and “asp-action” attributes. But the anchor TagHelper has much more to offer.

  • You can add route parameters by using the “asp-route-” prefix (example: asp-route-id = @ViewBag.PatientId).
  • You can use a named route with the “asp-route” attribute
  • You can force protocols like https by using for example: asp-protocol=”https” (and if you want to do this for a certain domain you use “asp-host”)
  • You can link to a specific part of your page by using “asp-fragment”

We’ll need a follow up to talk about the anchor Tag Helper separately.

Cache

Can be a useful Tag Helper for improving your performance by storing cache entries in local memory. This means that anything that will stop the host process will cause the cache entries to be lost. This is something to keep in mind when for instance your Azure instances get scaled. A scale down means losing the cache.

But if you bear this in mind it simply works by wrapping any portion of Razor View you want and storing that content in an instance of IMemoryCache. The simplest form looks like:

taghelpers_2

But you can do a lot more cool stuff with this, like having it vary by user, or by route. I’ll treat the cache Tag Helper in detail another time.

Form

The form Tag Helper reads much easier than the form HTML helper and looks at its most basic like:

taghelpers_2

The output would be:

taghelpers_2

Side note: As you can see the Tag Helper automatically renders an input element to create the anti-forgery token [7].

There are more options, but I’ll write about that separately.

Input

The input Tag Helper is one of the form elements and could be used to replace the HTML helper EditorFor. Imagine the following class:

	public class SomeModel
	{
		public string SomeString { get; set; }
	}

The input Tag Helper looks like:

taghelpers_2

This renders as:

taghelpers_2

The input Tag Helper will be described further another time.

Label

The label TagHelper is one of the form elements and could be used to replace the HTML helper LabelFor. Imagine the following class:

		public class SomeModel
	{
		[Display(Name=”Type something here”)]
		public string SomeString { get; set; }
	}

The label Tag Helper looks like:

taghelpers_2

This renders as:

taghelpers_2

Notice how the label Tag Helper is capable of automatically grabbing the value of the Name property of the Display attribute. And that is all there is to the label Tag Helper really.

Link, Script and Environment

The link and script Tag Helper also support globbing[2]. An interesting example could be:

taghelpers_2

This tells via globbing and the ‘asp-src-include’ Tag Helper attribute to include all ‘.js’ files in the ‘js’ of ‘wwwroot’. I’ve thrown ‘bootstrap.js’, ‘jquery.js’ and ‘npm.js’ in the ‘js’ folder at ‘wwwroot’ and therefor it produces the following output:

taghelpers_2

Pretty convenient right? You can also exclude files, reference files from hosted CDN and provide a fallback, and much more. You can even apply cache busting via the ‘asp-append-version’ attribute. More on these Tag Helpers in a seperate article.

Side note: Actually, when you use it exactly like the example above you’ll get an error, because bootstrap needs jQuery. You’ll want to reference hosted CDNs and provide fall-backs, for example:

taghelpers_2

But as I’ve written above, this deserves a separate article.

Let’s focus on the environment Tag Helper now instead. This Tag Helper works really well with the script and link Tag Helpers and is therefore included in this paragraph. With ASP.NET Core 1.0 you can set the environment to different stages: Development, Staging, and Production. And with the environment Tag Helper we could include all the unminified .js files in development, while using the minified versions in staging/production. Like this:

taghelpers_2

I’ve seen it most commonly used like this and I currently only use this Tag Helper for this reason.

Select and Option

A select is not really useful without options, so therefor they are described together in this section. A select will render as a drop down list and is the equivalent of the HTML helper ‘DropDownListFor’. So we have ‘SomeModel’ with ‘SomeString’ from our example. And in the ‘Index’ method of the ‘HomeController’ we add some options to a ViewBag property, like this:

taghelpers_2

You could use this Tag Helper as follows:

taghelpers_2

Which would render to the browser as:

taghelpers_2

And if the property at the asp-for is of type IEnumerable, then the Tag Helper would automatically render a multi-selectable dropdown.

TextArea

The text area Tag Helper is the replacement of the HTML helper ‘TextAreaFor’ and basically works the same as the input Tag Helper. To show how it works I’ve expended our ‘SomeModel’ with the property ‘SomeLargeString’ and a ‘MaxLength’ attribute:

taghelpers_2

You simply write the tag in HTML as follows:

taghelpers_2

This outputs the following HTML to the browser:

taghelpers_2

It plays along nicely again with the attributes like ‘MaxLength’, ‘Required’, etc., providing adequate attributes in HTML as can be seen in the rendered HTML above.

ValidationMessage and ValidationSummary

The textarea Tag Helper is capable of providing its own validation attributes, like ‘data-val-maxlength-max’, but it is also possible to explicitly set the validation with the validation message TagHelper:

taghelpers_2

SomeString could have the ‘Required’ attribute, like so:

taghelpers_2

The span will actually render to the browser as:

taghelpers_2

Don’t forget to change the post method in the HomeController:

taghelpers_2

Just make a simple Succes.cshtml for when the state of the model is valid. And when the required SomeString is not provided on the post the same page will return with the span rendered as:

taghelpers_2

But most of the time is prettier to summarize all validation messages and that is where the validation summary Tag Helper comes in:

taghelpers_2

You simply add the ‘asp-validation-summary’ to a div tag and it works. The attribute requires a ValidationSummary enumeration which could be ‘All’, ‘ModelOnly’ or ‘None’. Why you would ever want to choose ‘None’ is beyond me, just don’t add a validation summary then! But the ‘ModelOnly’ is to only include validations on model level and ‘All’ is to include both model level and property level. It will render as:

taghelpers_2

And when SomeProperty is not provided while it has the ‘Required’ attribute it’ll render as:

taghelpers_2

These were the provided TagHelpers by [MVC 6], now let’s see how you can create one yourself!

Custom TagHelpers

You can expand existing HTML elements with a TagHelper or you can make your own TagHelper completely from scratch. This practice is called ‘authoring TagHelpers’ [3]. The first kind will function as simply another attribute on the HTML element like we saw with the HTML Helpers ‘asp-action’ and ‘asp-helper’ at the anchor tag. The process of making your own TagHelper is very similar in both cases. Let’s first create a TagHelper that you can use as attributes.

Attributes

As a nice short example we can have a span tag that you could provide a message and a level for how big you want that message displayed, like this:

taghelpers_2

To make this HTML work I wrote the following code which we’ll walk through:

taghelpers_2

First of all you need to derive from the class TagHelper to make a TagHelper (you could theoretically also implement ITagHelper).
With the HtmlTargetElement attribute above the MessageTagHelper class you can specify to which HTML element(s) this TagHelper applies to. The Attributes property is a comma separated list with which you can make certain attributes required. I wanted the MessageValue property required as can be seen in the code above. You can decorate the properties with the attribute HtmlAttributeName to give them a clear name in HTML.

Side note: This attribute is not required as the property’s name in camel case will be converted to ‘lower kebab case’ as a convention (so for example: “message-value”). But in this example we adhere to the ‘asp-‘ convention for TagHelpers without messing up the names of the properties.

Now you need to override either the ‘Process’ method or the ‘ProcessAsync’ method if you expect to handle large files or something. This is an extremely simple TagHelper so the sequential variant will be more than sufficient.
You get passed along the TagHelperContext and the TagHelperOutput parameter. The TagHelperContext contains all kinds of information about the element this TagHelper targets. So in our case the concerning span element. I didn’t need the parameter in this example, but you’ll probably need it as the TagHelper becomes more complex. The TagHelperOutput parameter is used all the time as it is used to transform, append and remove the HTML.

The TagName is changed to “div” (to be honest this is just because the h tags are block elements and the span tag is an inline element). The TagMode is set to StartTagAndEndTag to enable you to use the span in HTML as a self-closing element. Then you see some code to deal with the given level, nothing fancy. At the end you can see the content appended to the output’s ‘Content’. You also have ‘PreContent’ and ‘PostContent’, which I didn’t need for this simple example. But exploring the TagHelperOutput parameter is really going to help you make complex TagHelpers.

When we run this the actual HTML rendered will be:

taghelpers_2

As have been said you can also create HTML elements completely from scratch.

Elements

You can also create entire new HTML tags. For this example we’ll have a look at Bootstrap styled panels:

taghelpers_2

It would be better to be able to write:

taghelpers_2

It is more in line with HTML5’s semantic way of coding the layout. It reads better. So let’s make it happen. All we need is the following code really:

taghelpers_2
taghelpers_2

Let’s go through the code and start at PanelTagHelper. The TagHelper suffix is discarded and the rest is used as the name for the TagHelper. So in this case the HTML element is called “panel”. We use the RestrictChildren attribute above the class declaration to restrict the types of HTML elements that can be used in between the ‘panel’ tag. Notice for instance how it says ‘panel-header’. Why is that not ‘panelheader’, because the class’s name is PanelHeaderTagHelper? Well like properties, which is said earlier, class names also follow the lower kebab case. And because we didn’t define another name, it will go by default.

Side note:TagHelper suffix is not required but considered a best practice convention.

This time the ‘ProcessAsync’ method is overridden. It is still not necessary, but I wanted to show you what it looks like. There we use ‘TagName’ to change the tag to a ‘div’ tag. And we make sure ‘panel’ is in the class attribute, so Bootstrap’s ‘panel’ styling can kick in.

And you can see this basically in all the ProcessAsync methods. Just changing the class to the appropriate one (panel-header, panel-body, panel-footer).

Point of interest though is the HtmlTargetElement attribute. It is there to make sure this TagHelper is used on the right HTML element. It has a ParentTag attribute to tell that it only applies if the ‘panel-header’ (or body/footer) is in between the ‘panel’ tag. But this is unfortunately not enough. See header, body and footer all apply to this rule and we would end up with divs that have all three classes. So we also need to tell exactly what HTML tag the TagHelper applies to, to prevent this from happening.

Side note: You can find much more (and better) TagHelper samples on Dave Paquette’s GitHub [5].

Conclusion

I hope we can all agree that the ideal webpage is written in nothing but HTML and Javascript. TagHelpers remove awkward C# code from our Razor Views making it look more like straight up HTML. This is especially great for the non-programmers, like designers, who just want to design great looking pages in HTML and CSS without being tangled in webs of backend code. But also for the full-stack developer who does enough context switching as is. Thank you for reading and check out the source code for this article on GitHub [4]. And don’t forget to check out SDN magazine #128[8].

Links

  1. TagHelpers vs HTML helpers: http://docs.asp.net/projects/mvc/en/latest/views/tag-helpers/intro.html#tag-helpers-compared-to-html-helpers
  2. Glob notation: https://en.wikipedia.org/wiki/Glob_%28programming%29
  3. Authoring TagHelpers: http://docs.asp.net/projects/mvc/en/latest/views/tag-helpers/authoring.html
  4. My GitHub: https://github.com/dannyvanderkraan/taghelpers
  5. Dave Paquette’s TagHelper samples: https://github.com/dpaquette/TagHelperSamples
  6. _ViewImports.cshtml: http://www.exceptionnotfound.net/the-viewimports-cshtml-file-setting-up-view-namespaces-in-mvc-6/
  7. Anti forgery token: http://blog.stevensanderson.com/2008/09/01/prevent-cross-site-request-forgery-csrf-using-aspnet-mvcs-antiforgerytoken-helper/
  8. http://www.sdn.nl/portals/1/magazine/SDN-Magazine-128.pdf
Advertisements

About Danny

Bachelor in Commercial ICT MCTS Winforms .NET 2.0 MCTS ASP.NET 3.5 PSM I
This entry was posted in ASP.NET Core 1.0, ASP.NET MVC, DNX, Uncategorized and tagged , , . Bookmark the permalink.

4 Responses to ASP.NET Core 1.0: Goodbye HTML helpers and hello TagHelpers!

  1. Naibedya Kar says:

    Nicely presented, thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s