Introduction
TagHelper is a new feature in ASP.NET MVC. If you haven’t already heard, TagHelpers have gained quite a bit of discussion among the techies, with some claiming the return of server side controls. Anyone remember:
<asp:TextBox id="txt1" runat="server">
I personally don’t see a problem with TagHelpers, it’s just another simplification in mixing HTML with server generated content without the baggage of control life cycles, view state, events, etc. In fact, I believe it is a bit more cleaner approach.
Disclaimer
This article is based on the beta version of ASP.NET 5 (or vNext). Things can and will change, so some of the information about TagHelpers might be out of date.
Purpose
There are already articles on TagHelpers – the brilliant ones are by Scott Hanselman and Jeffrey T. Fritz (see resources). This article adds on top as:
- It provides more up to date information based on the latest development build (v1.0.0-rc1-10798)
- It gives developer focused view of TagHelpers with more examples
- We go through building a sample project. It took me some time to figure out how the various pieces fit together as everything is constantly changing, and not well documented so it might save time for others
What are TagHelpers?
TagHelpers allow preprocessing of HTML attributes with server side content - that is:
<a asp-controller="Home" asp-action="About">About</a>
gets transformed to:
<a href="/Home/About">About</a>
Before dwelling deeper, let’s see how the current version of MVC behaves. This will also give us an appreciation of the need for TagHelpers.
In ASP.NET MVC 5, if we wanted to declare a link to another page (anchor tag), there are several options.
ActionLink
Perhaps the most common is using the HTML helper ActionLink
method. There are several overloads, I am using the one that will let me set the action, controller and HTML attributes.
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
Object routeValues,
Object htmlAttributes
)
A simple usage would be:
@Html.ActionLink("About this site",
"About","Home",null,new { @class="someClass"})
Razor & HTML
Another option would be to mix n match this in razor syntax.
<a href="@Url.Action("About",
"Home")" class="someClass">About this site</a>
We can also create our own Razor helpers or HTML helper extension methods.
While this is not bad, our scenario is simplistic, I am sure all of us know how tricky it can be to mix n match razor syntax with HTML. Another way of looking at this is, that in the first example we went razor first and added HTML attributes, whereas in the second instance, we went HTML first and added razor syntax.
AnchorTagHelper
This is where TagHelper come into the picture as we don't have to leave the comfort of HTML, and at the same time, we can utilize the power of C# server side. Considering the same example above, we can rewrite the anchor tag as:
<a asp-controller="Home" asp-action="About" class="someClass">About this site</a>
To simplify what happens next - MVC will pass this anchor to a C# class (AnchorTagHelper
) that will inspect the attributes and output the server generated content. It’s a cleaner way of mixing HTML and C# code.
This is different to the ASP.NET server side controls even though the syntax/concept might look extremely familiar. The main differences are:
- No life cycle events
- No view state
- No control state
Note: Some articles will refer to the use of controller and action attributes. However, there was a discussion on this issue and looks like out of the three popular approaches, the one with prefixing asp- has been selected in the current build. The other two options were to use @controller
or just controller
.
TagHelpers Namespace
ASP.NET already comes with a set of built TagHelpers. They live in the Microsoft.AspNet.Mvc.TagHelpers
namespace and consists of:
Anchor
Input
Label
Select
TextArea
ValidationMessage
ValidationSummary
Form
We will take a quick look at all of these and their basic usage.
AnchorTagHelper
This tag will be applied to <a>
anchor elements. At the moment, it is supports the following attributes:
asp-action
asp-controller
asp-fragment
asp-host
asp-protocol
asp-route
All of these are used to generate a URL for the href
attribute of the anchor
tag.
<a asp-controller="Home" asp-action="About">About</a>
If the href
is already set in the <a> anchor
HTML element, an exception will be thrown if the tag helper attributes are set. So for example, the below will throw an exception.
<a href="/Home/Index" asp-controller="Home" asp-action="Index">Home</a>
InputTagHelper
As the name suggests, this TagHelper
is applied to <input>
HTML elements. It is similar to the TextBoxFor
HMTL extension method as it generates an input element bound to a property in the model. The input
tag supports the following attributes:
asp-for
asp-format
The 'asp-for
' attribute refers to a property in the model and is also used in various other tag helpers
The 'asp-format
' applies the format after the value has been retrieved. This is useful for currencies, or date time values. For example (Birthday
is a property on the model of type DateTime
).
<input asp-for="Birthday" asp-format="{0:yyyy-MM-dd}" />
Note: The asp-for
is of type ModelExpression
, this is a new class in ASP.NET MVC 6 (vNext). The constructor takes a string
parameter that evaluates to a property in the Model. We can also refer to nested objects - for example:
<input asp-for="Address.Street" type="text" /></div>
Contrast this with TextBoxFor
:
@Html.TextBoxFor(model => model.Birthday)
At the moment, there is a problem with the MVC 6 model binder, as entering a invalid date (abc
) will display an exception message.
The parameter conversion from type 'System.String
' to type 'System.DateTime
' failed. See the inner exception for more information.
In the MVC 5, the exception message is reprocessed to a more user friendly version as:
The value 'abc' is not valid for Birthday
LabelTagHelper
Same purpose as the HtmlExtension.LabelFor
method - has only one attribute 'asp-for
' used to define a property in the model (as discussed in InputTagHelper
). It is applied to the <label>
element.
<label asp-for="Birthday" />
SelectTagHelper
The SelectTagHelper
applies to the <select>
element and supports two attributes 'asp-for
' and 'asp-items
'. As discussed before, asp-for
is used to define a property in the model, whereas asp-items
is used to define a collection of values of type IEnumerable<SelectListItem>.
<select asp-for="Country" asp-items="ViewBag.Countries">
To add a default item, we can do this:
<select asp-for="Country" asp-items="ViewBag.Countries">
<option selected="selected" value="">Choose Country</option>
</select>
Just for comparison, the HtlmHelper
way of doing this:
@Html.DropDownListFor(m=>m.Country, (IEnumerable<SelectListItem>)ViewBag.Countries)
The asp-items
can evaluate to anything available to the view - it could be a property on the object, variable, etc.
We could declare in the view a variable:
@{
SelectListItem[] items =
{
new SelectListItem() { Text = "item 1" },
new SelectListItem() { Text = "item 2" }
};
}
and then use that as asp-items
:
<select asp-for="Country" asp-items="items">
TextAreaTagHelper
Processes the <textarea>
element - supports only one attribute asp-for
, where Information
is a property on the:
<textarea asp-for="Information"></textarea>
ValidationMessageTagHelper
This tag helper is used for displaying the validation-error message. It has the same purpose as the ValidationMessageFor
HTML Helper. The interesting question here might be which HTML element it applies to? The ValidationMessageFor
extension method generated a <span>
element, similarly the ValidationMessageTagHelper
applies on the <span>
element.
<span asp-validation-for="Birthday" />
It has only one attribute asp-validation-for
, which represents a field on the Model
.
<input asp-for="Birthday" asp-format="{0:yyyy-MM-dd}" />
<span asp-validation-for="Birthday" />
Note: There is a new attribute called TagName
that links a C# class with the HTML tag on which it is applied.
[TagName("span")]
public class ValidationMessageTagHelper : TagHelper
ValidationSummaryTagHelper
Used to display the summary of validation errors - like the HTML extension method ValidationSummary
. It only supports one attribute asp-validation-summary
that can have one of the following values:
None
: No validation messages displayed
ModelOnly
: Displays only the Model
errors - not the property ones
All
: Displays all messages
The asp-validation-summary
is applied to a div
element.
<div class="validation" asp-validation-summary="ModelOnly"/>
FormTagHelper
Used to generate a <form>
element. It supports the following attributes which are self explanatory.
asp-action
asp-controller
asp-anti-forgery
This is similar to the HTML helper BeginForm
method.
<form asp-action="FormSave"
asp-controller="Home" asp-anti-forgery="true">
If the action attribute is specified on the form
tag with the asp-
attributes, it will throw an InvalidOperationException
.
Putting it all together
<form asp-action="FormSave"
asp-controller="Home" asp-anti-forgery="true" method="post" >
<div>
<label asp-for="Name" />
<input asp-for="Name" type="text" />
<span class="validation" asp-validation-for="Name" />
</div>
<strong>Validation Errors</strong>
<div class="validation" asp-validation-summary="ModelOnly"/>
</form>
Custom TagHelpers
TagHelpers
can be extended – like everything else. So we can create our own TagHelpers
to augment our HTML controls. There is already an article discussing creation of TagHelper
for Kendo date picker.
How to Start using TagHelpers
The two basic steps required to use TagHelpers
is:
- Add reference to
Microsoft.AspNet.Mvc.TagHelpers
- Add
@addtaghelper "Microsoft.AspNet.Mvc.TagHelpers"
keyword in our view where we want to use the tag helpers.
- The
@addtaghelper
can also be added to the _Viewstart.cshtml so it is available to all the views.
If you are having difficulties in getting this to work, follow the sample project below.
Project
Let’s now dig into a small project that will use both these attributes. The project was built using Visual Studio 2015 preview. There is also a section on building and running this without using Visual Studio near the end.
Source
The project source is available at github here.
If you download the project and build using Visual Studio, please set the latest version of the KRE-CLR runtime otherwise the project will not build, following the steps in the section Latest Version below.
If you receive the error, "Could not find the Microsoft.AspNet.Loader.Interop nuget package in the packages folder of the current solution
" - close Visual Studio and open the project again, it should work.
Latest Version
It’s important to realize, that ASP.NET vNext is in a constant state of changes & updates. As such, when I downloaded and installed Visual Studio, the TagHelper
changes were not available in the CLR. So, there are some perquisites to connect to the development feed and download the latest NuGet packages.
Install KVM
We will need to get the latest version of the CLR runtime. For that, we need to install KVM – this can be done by running the following in command prompt:
<code>@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex
((new-object net.webclient).DownloadString
('https://raw.githubusercontent.com/aspnet/Home/master/kvminstall.ps1'))"
Updating KRE Runtime
I found that KVM was using the NuGet feed to install the latest version of the runtime (https://www.nuget.org/api/v2) which at this time did not contain the TagHelper
changes. I had to change the feed to https://www.myget.org/F/aspnetvnext/api/v2/.
I looked at the kvm.ps1 file (at %userprofile%\.kre\bin\) and found it was using an environment variable KRE_NUGET_API_URL
. So I had set it by:
- Opening a new power shell window
- Running the following command:
$env:KRE_NUGET_API_URL = “https://www.myget.org/F/aspnetvnext/api/v2/”
In the same PowerShell window, run the following command to download and install the latest version of the CLR from the ASP.NET development feed.
kvm upgrade
To see the installed CLR, run the following command:
kvm list
Creating a New Project
Create a new ASP.NET Web Application. Check the “Create directory for solution”, so everything required is inside the solution. In the next screen, select the ASP.NET 5 Empty project.
Update NuGet Feed in Visual Studio
We need to update the NuGet feed so the latest .NET packages can be downloaded. This is done by going into Tools -> NuGet Package Manager-> Package Manager Settings. Add a new package source, set the source as https://www.myget.org/F/aspnetvnext/api/v2/ and Name as ASP.NET vNext, and click Update.
Set KRE CLR Version
By default, Visual Studio uses the default KRE version from NuGet feed which at the time of writing this article is 1.0.0 beta 1. Not all the new options are available in this version. We will change this to the latest version which (now) is KRE-CLR-x86.1.0.00-rc1-10781. If you only see one version, make sure to have followed the steps to update the KRE. Also, the version you will see depends on the current build.
Add References
The next step is to add the references. Open project.json and add the following under dependencies.
"Microsoft.AspNet.Server.IIS": "1.0.0-rc1-10778",
"Microsoft.AspNet.Server.WebListener" : "1.0.0-rc1-11230"
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-12143",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-12143"
The first dependency allows hosting our application in IIS, the second one support self hosting. The other two are self explanatory.
Enable MVC
In Startup.cs:
Add the following using
statement:
using Microsoft.Framework.DependencyInjection;
Add the following in the method Configure
:
app.UseServices(m => { m.AddMvc(); });
app.UseMvc();
Add Controllers
Create a folder called Controllers, and add a new controller class called HomeController
. Add the following two methods:
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
return View();
}
Add Views
Create a folder called Views, and inside, add another folder called Home. Inside Home add two new MVC View Pages:
- Index.cshtml
- About.cshtml
Index.cshtml
@addtaghelper "Microsoft.AspNet.Mvc.TagHelpers"
<h2>TagHelper test website</h2>
<ul>
<li>
<a asp-controller="Home" asp-action="About">Anchor tag</a>
</li>
</ul>
About.cshtml
@addtaghelper "Microsoft.AspNet.Mvc.TagHelpers"
<h2>About page</h2>
<p>This website is created to show case the new TagHelper functionality in Asp.Net vNext
</p>
<p>
<a asp-controller="Home" asp-action="Index">Back</a>
</p>
Run
If you run the project, you should be able to see the tag helpers in action.
Without using Visual Studio
Let’s say you are going all ninja, and would like to build (& run) the project without using Visual Studio. If the answer is yes, keep reading.
Note: Please update KRE to the latest version following the steps mentioned earlier in Install KVM.
Update NuGet for KPM
If you are planning on restoring packages using KPM command line, it is important to update the NuGet feed source. This can be done as follows:
- Browse to %appdata%\NuGet and open the file Nuget.config.
- Under package sources, add a new node as follows:
<add key="aspnet5 " value="https://www.myget.org/F/aspnetvnext/api/v2/" />
- Save
Run
- Open a new powershell
- Browse to the src folder for the project – for example, if the project is under c:\Development, browse to c:\Development\TagHelperSample\src\TagHelperSample.
- Change the powerscript to use the latest version of KRE CLR. You can run the command kvm list to see the available CLRs and choose the latest version from the dev branch. If you only see one CLR – it is probably because KRE feed has not been updated. Please do this by following the steps mentioned earlier in Install KVM.
kvm use 1.0.0-rc1-10781
- Restore the packages by running the following command:
kpm restore
- Run the project by using the following command:
k Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener
--server.urls http://localhost:5001
If you browse to http://localhost:5001 - the website should be available.
Resources
History
- 16/12/2014 - First version
- 18/12/2014 - Added additional details about
TagHelper
classes