Creating custom ASP.NET Core Tag Helpers

Updated Nov 22, 2015: Updated to account for changes in ASP.NET 5 RC1
Updated May 5, 2016: Updated to account for changes in ASP.NET Core RC2

In the last few blog posts, I have spent some time covering the tag helpers that are built in to ASP.NET Core MVC. While the built in tag helpers cover a lot of functionality needed for many basic scenarios, you might also find it beneficial to create your own custom tag helpers from time to time.

In this post, I will show how you can easily create a simple tag helper to generate a Bootstrap progress bar. NOTE: Thank you to James Chambers for giving me the idea to look at bootstrap components for ideas for custom tag helpers.

Based on the documentation for the bootstrap progress bar component, we need to write the following HTML to render a simple progress bar:

<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%;">
<span class="sr-only">60% Complete</span>
</div>
</div>

This is a little verbose and it can also be easy to forget some portion of the markup like the aria attributes or the role attribute. Using tag helpers, we can make the code easier to read and ensure that the HTML output is consistent everywhere we want to use a bootstrap progress bar.

Choosing your syntax

With tag helpers, you can choose to either create your own custom tag names or augment existing tags with special tag helper attributes. Examples of custom tags would be the environment tag helper and the cache tag helper. Most of the other built in MVC tag helpers target existing HTML tags. Take a look at the input tag helper and the validation tag helper for examples.

At this point, I’m not 100% sure when one is more appropriate than the other. Looking at the built in MVC tag helpers, the only tag helpers that target new elements are those that don’t really have a corresponding HTML element that would make sense. Mostly, I think it depends on what you want your cshtml code to look like and this will largely be a personal preference. In this example, I am going to choose to target the <div> tag but I could have also chosen to create a new <bs-progress-bar> tag. The same thing happens in the angularjs world. Developers in angularjs have the option to create declare directives that are used as attributes or as custom elements. Sometimes there is an obvious choice but often it comes down to personal preference.

So, what I want my markup to look like is this:

<div bs-progress-value="@Model.CurrentProgress" 
bs-progress-max="100"
bs-progress-min="0">

</div>

Now all we need to do is create a tag helper turn this simplified markup into the more verbose markup needed to render a bootstrap progress bar.

Creating a tag helper class

Tag helpers are pretty simple constructs. They are classes that inherit from the base TagHelper class. In this class, you need to do a few things.

First, you need to specify a TargetElement attribute: this is what tells Razor which HTML tags / attributes to associate with this tag helper. In our case, we want to target any <div> element that has the bs-progress-value element.

[HtmlTargetElement("div", Attributes = ProgressValueAttributeName)]
public class ProgressBarTagHelper : TagHelper
{
private const string ProgressValueAttributeName = "bs-progress-value";
//....
}

The Attributes parameter for the TargetElement is a comma separated list of attributes that are _required _for this tag helper. I’m not making the bs-progress-min and bs-progress-max attributes required. Instead, I am going to give them a default of 0 and 100 respectively. This brings us to the next steps which is defining properties for any of the tag helper attributes. Define these as simple properties on the class and annotate them with an HtmlAttributeName attribute to specify the attribute name that will be used in markup.

[HtmlTargetElement("div", Attributes = ProgressValueAttributeName)]
public class ProgressBarTagHelper : TagHelper
{
private const string ProgressValueAttributeName = "bs-progress-value";
private const string ProgressMinAttributeName = "bs-progress-min";
private const string ProgressMaxAttributeName = "bs-progress-max";

[HtmlAttributeName(ProgressValueAttributeName)]
public int ProgressValue { get; set; }

[HtmlAttributeName(ProgressMinAttributeName)]
public int ProgressMin { get; set; } = 0;

[HtmlAttributeName(ProgressMaxAttributeName)]
public int ProgressMax { get; set; } = 100;

//...
}

These attributes are strongly typed which means Razor will give you errors if someone tries to bind a string or a date to one of these int properties.

Finally, you need to override either the Process or the ProcessAsync method. In this example, the logic is simple and does not require any async work to happen so it is probably best to override the Process method. If the logic required making a request or processing a file, you would be better off overriding the ProcessAsync method.

[HtmlTargetElement("div", Attributes = ProgressValueAttributeName)]
public class ProgressBarTagHelper : TagHelper
{
private const string ProgressValueAttributeName = "bs-progress-value";
private const string ProgressMinAttributeName = "bs-progress-min";
private const string ProgressMaxAttributeName = "bs-progress-max";

/// <summary>
/// An expression to be evaluated against the current model.
/// </summary>
[HtmlAttributeName(ProgressValueAttributeName)]
public int ProgressValue { get; set; }

[HtmlAttributeName(ProgressMinAttributeName)]
public int ProgressMin { get; set; } = 0;

[HtmlAttributeName(ProgressMaxAttributeName)]
public int ProgressMax { get; set; } = 100;

public override void Process(TagHelperContext context, TagHelperOutput output)
{

var progressTotal = ProgressMax - ProgressMin;

var progressPercentage = Math.Round(((decimal) (ProgressValue - ProgressMin) / (decimal) progressTotal) * 100, 4);

string progressBarContent =
$@"<div class='progress-bar' role='progressbar' aria-valuenow='{ProgressValue}' aria-valuemin='{ProgressMin}' aria-valuemax='{ProgressMax}' style='width: {progressPercentage}%;'>
<span class='sr-only'>{progressPercentage}% Complete</span>
</div>";


output.Content.AppendHtml(progressBarContent);

string classValue;
if (output.Attributes.ContainsName("class"))
{
classValue = string.Format("{0} {1}", output.Attributes["class"].Value, "progress");
}
else
{
classValue = "progress";
}

output.Attributes.SetAttribute("class", classValue);
}
}

The Process method has 2 parameters: a TagHelperContext and a TagHelperOutput. In this simple example, we don’t need to worry about the TagHelperContext. It contains information about the input element such as the attributes that were specified there and a unique ID that might be needed if multiple instances of the tag helpers were used on a single page. The TagHelperOutput is where we need to specify the HTML that will be output by this tag helper. We start by doing some basic math to calculate the percentage complete of the progress bar. Next, I used a string.Format to build the inner HTML for the bootstrap progress bar with the specified min, max, value and calculated percentages. I add this to the contents of the output by calling output.Content.Append. The last step is to add class=”progress” to the outer div. I can’t just add the attribute though because there is a chance that the developer has already specified another class for this div (it is possible that we want the output to be class=”green progress”.

If you need to build more complicated HTML in the content, you should consider using the TagBuilder class. If a tag helper grows too complex, you might want to consider creating a View Component instead.

Finally, we should add some argument checking to make sure that the Min / Max and Value properties are appropriate. For example, the ProgressMin value should be less than the ProgressMax value. We can throw argument exceptions to clearly indicate errors. Here is the finally implementation of the ProgressBarTagHelper:

[HtmlTargetElement("div", Attributes = ProgressValueAttributeName)]
public class ProgressBarTagHelper : TagHelper
{
private const string ProgressValueAttributeName = "bs-progress-value";
private const string ProgressMinAttributeName = "bs-progress-min";
private const string ProgressMaxAttributeName = "bs-progress-max";

/// <summary>
/// An expression to be evaluated against the current model.
/// </summary>
[HtmlAttributeName(ProgressValueAttributeName)]
public int ProgressValue { get; set; }

[HtmlAttributeName(ProgressMinAttributeName)]
public int ProgressMin { get; set; } = 0;

[HtmlAttributeName(ProgressMaxAttributeName)]
public int ProgressMax { get; set; } = 100;

public override void Process(TagHelperContext context, TagHelperOutput output)
{

if (ProgressMin >= ProgressMax)
{
throw new ArgumentException(string.Format("{0} must be less than {1}", ProgressMinAttributeName, ProgressMaxAttributeName));
}

if (ProgressValue > ProgressMax || ProgressValue < ProgressMin)
{
throw new ArgumentOutOfRangeException(string.Format("{0} must be within the range of {1} and {2}", ProgressValueAttributeName, ProgressMinAttributeName, ProgressMaxAttributeName));
}
var progressTotal = ProgressMax - ProgressMin;

var progressPercentage = Math.Round(((decimal) (ProgressValue - ProgressMin) / (decimal) progressTotal) * 100, 4);

string progressBarContent =
$@"<div class='progress-bar' role='progressbar' aria-valuenow='{ProgressValue}' aria-valuemin='{ProgressMin}' aria-valuemax='{ProgressMax}' style='width: {progressPercentage}%;'>
<span class='sr-only'>{progressPercentage}% Complete</span>
</div>";


output.Content.AppendHtml(progressBarContent);

string classValue;
if (output.Attributes.ContainsName("class"))
{
classValue = string.Format("{0} {1}", output.Attributes["class"].Value, "progress");
}
else
{
classValue = "progress";
}

output.Attributes.SetAttribute("class", classValue);

base.Process(context, output);
}
}

Referencing the custom Tag Helper

Before we can start using our custom tag helper, we need to add a reference to the tag helpers in the current assembly using the @addTagHelper Razor command. We can do this in individual Razor files or we can add it the _GlobalImports.cshtml file so it is applied everywhere:

@using WebApplication3
@using WebApplication3.Models
@using Microsoft.Framework.OptionsModel
@using Microsoft.AspNet.Identity
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
@addTagHelper "*, WebApplication3"

Now we can reference the tag helper in any of our Razor views. Here is an example binding the PercentComplete property from the model to the ProgressValue property of the tag helper.

<div bs-progress-value="@Model.PercentComplete"></div>

Here is another example that would display progress for a 5 step process:

<div bs-progress-min="1"
bs-progress-max="5"
bs-progress-value="@Model.CurrentStep">

</div>

Unit Testing

Tag Helpers are easy enough to unit test and I would definitely recommend that you do test them. Take a look at these examples in the ASP.NET Core MVC repo for reference.

Conclusion

By creating this simple tag helper, we are able to greatly simplify the Razor code required to create a bootstrap progress bar with a value from the our model. As a result, our Razor view is easier to understand and we can ensure a consistent output for all progress bars in our app. If we needed to change the way we rendered progress bars, we would only need to change the code in one place. This is of course a relatively simple example but I think it shows the potential for tag helpers in MVC.

Sept 2015 Update

The official ASP.NET documentation is now available for creating custom tag helpers. Check it out here.

I have created a Github repository for this and other useful tag helpers. Check it out here. Thanks to Rick Anderson and Rick Strahl for contributing. Let me know if you have ideas for other tag helpers that you would like to see in there.

ASP.NET Core MVC Cache Tag Helper

In this post in my series exploring the ASP.NET Core MVC tag helpers, I will dig into the Cache Tag Helper. The Cache Tag Helper is a little different than most of the other tag helpers we talked about because it doesn’t target a standard HTML tag. Instead, it wraps arbitrary content and allows those contents to be cached in memory based on the parameters you specify.

How it works?

Simply wrap the contents you want cached with a <cache> tag and the contents of the tag will be cached in memory. Before processing the contents of the cache tag, the tag helper will check to see if the contents have been stored in the MemoryCache. If the contents are found in the cache, then the cached contents are sent to Razor. If the contents are not found, then Razor will process the contents and the tag helper will store it in the memory cache for next time. By default, the cache tag helper is able to generate a unique ID based on the context of the cache tag helper.

Here is a simply example that would cache the output of a view component for 10 minutes.

<cache expires-after="@TimeSpan.FromMinutes(10)">
@Html.Partial("_WhatsNew")
*last updated @DateTime.Now.ToLongTimeString()
</cache>

The Cache tag will not be included in the generated HTML. It is purely a server side tag. In the example above, only the results of the WhatsNew partial view and the *last updated text would be sent to the browser. Any subsequent requests within the 10 minute span simply return the cached contents instead of calling the partial view again. On the first request after the 10 minutes has passed, the contents would be regenerated and cached again for another 10 minutes.

Cache Expiry

If no specific expiry is specified, the contents will be cached as long as the memory cache decides to hang on to the item which is likely the lifetime of the application. Chances are this is not the behaviour you want. You will likely want to use one of the 3 options for expiring the cached contents for the Cache Tag Helper: expires-after, expires-on and expires-sliding.

expires-after

Use the expires-after attribute to expire the cache entry after a specific amount of time has passed since it was added to the cache. This attribute expects a TimeSpan value. For example, you expire an item 5 seconds after it was cached:

<cache expires-after="@TimeSpan.FromSeconds(5)">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

expires-on

Use the _expires-on _attribute to expire the cache entry at a specific time. This attribute expects a DateTime value. For example, imagine your system has some backend processing that you know will be updated by the end of each day. You could specify the cache to expire at the end of the day as follows:

<cache expires-on="@DateTime.Today.AddDays(1).AddTicks(-1)">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

expires-sliding

Use the expires-sliding attribute to expire the cache entry after it has not been accessed for a specified amount of time. This attribute expects a TimeSpan value.

<cache expires-sliding="@TimeSpan.FromMinutes(5)">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

 

Vary-by / Complex Cache Keys

The cache tag helper builds cache keys by generating an id that is unique to the context of the cache tag. This ensures that you can have multiple cache tags on a single page and the contents will not override each other in the cache. You can also tell the tag helper to build more complex cache keys using a combination of the vary-by attributes. Building these complex keys allows the cache tag helper to cache different contents for different requests based on nearly any criteria you can conceive. A very simple example is caching different contents for each user by adding the vary-by-user attribute:

<cache vary-by-user="true"> 
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

You can specify any combination of vary-by attributes. The cache tag helper will build a key that is a composite of the generated unique id for that tag plus all the values from the vary-by attributes.

vary-by-user

Use this attribute to cache different contents for each logged in user. The username for the logged in user will be added to the cache key. This attribute expects a boolean value. See example above.

vary-by-route

Use this attribute to cache different contents based on a set of route data parameters. This attribute expects a comma-separated list of route data parameter names. The values of those route parameters will be added to the cache key.

For example, the following cache tag would cache different contents based on the _id_ route parameter:

<cache vary-by-route="id">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

vary-by-query

The vary-by-query attribute allows you to cache different contents based on the query parameters for the current request. This attribute expects a comma-separated list of query string parameter names. The value of those query string parameters will be added to the cache key.

For example, the following cache tag would cache different contents for each unique value of the search query parameter:

<cache vary-by-query="search">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

The vary-by-cookie attributes allows you to cache different contents based on values stored in a cookie. This attribute expects a comma-separated list of cookie names. The values of the specified cookie names will be added to the cache key.

For example, the following cache tag would cache different contents based on the value of the MyAppCookie.

<cache vary-by-cookie="MyAppCookie">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

vary-by-header

The vary-by-header attribute allows you to cache different contents based on the value of a specific request header. This attribute expects a single header name. For example, the following cache tag would cache different results based on the User-Agent header:

<cache vary-by-header="User-Agent">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

vary-by

Finally, the vary-by attribute allows you to cache different contents based on any arbitrary string value. This attribute can be used as a fall-back in case any of the other vary-by attributes do not meet your needs.

For example, the following cache tag would cache different results based on the value of a ProductId that is available on the ViewBag:

<cache vary-by="@ViewBag.ProductId">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

Complex Keys

As mentioned earlier, you can specify any number of vary-by parameters and the cache tag helper will build a composite key. Here is an example of a cache tag that will cache different results for each user and _id_ route parameter:

<cache vary-by-user="true" vary-by-route="id">
<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

Cache Priority

The contents of a cache tag are stored in an IMemoryCache which is limited by the amount of available memory. If the host process starts to run out of memory, the memory cache might purge items from the cache to release memory. In cases like this, you can tell the memory cache which items are considered a lower priority using the priority attribute. For example, the following cache tag is specified as low priority:

@using Microsoft.Framework.Caching.Memory

<cache vary-by-user="true"
priority="@CachePreservationPriority.Low">

<!--View Component or something that gets data from the database-->
*last updated @DateTime.Now.ToLongTimeString()
</cache>

Possible values for CacheItemPriority are Low, Normal, High and NeverRemove.

Limitations

The CacheTagHelper implementation uses an instance of an IMemoryCache which stores cache entries in memory in the local process. Anything that causes the host process to shutdown / restart will cause a full loss of all entries in the cache. For example, restarting an IIS App Pool or scaling down an Azure instance would cause the memory cache to reset. In this case, the CacheTagHelper would rebuild the contents on the next request. In a cloud service like Azure you never know when your website might get moved to a new server so this could happen at any time. The MemoryCache is not a durable storage mechanism so it is important not to treat it as one.

Another important scenario to consider is when you have multiple load balanced servers. You might get strange / unexpected results if you have server affinity / application request routing.aspx) turned off. The MemoryCache is not a distributed cache. Each server would have it’s own memory cache with potentially different contents for each cache tag helper. If the client refreshes a page 3 times and those requests are routed to 3 different servers, then the client could potentially see 3 different contents. Depending on the scenario, this could be very confusing for the user. The solution here would be to avoid turning off ARR / server affinity in a load balanced deployment scenario. By turning this feature on you will ensure that a specific client’s requests are always routed to the same server.

Conclusion

The Cache cache tag helper is one of the more unique tag helpers in ASP.NET Core MVC. It provides a flexible and convenient approach to caching the output of a portion of a page and can be a useful tool for improving performance of MVC applications.

May 4, 2015: Updated with Limitations sections as suggested by Rick Anderson in the comments

ASP.NET Core MVC Anchor Tag Helper

In today’s post in my series on the ASP.NET Core MVC Tag Helpers, I will explore the Anchor tag helper. This tag helper is used generate href attributes to link to a particular controller action or MVC route.

This tag helper is an alternative to using `@Html.ActionLinkor@Url.Action` helper methods. For example, the following code in a Razor view:

@Html.ActionLink("Register", "Register", "Account")

<a href="@Url.Action("Register", "Account")">Register</a>

…would generate 2 identical anchor html tags.

<a href="/Account/Register">Register</a>
<a href="/Account/Register">Register</a>

Using the anchor tag helper, we can generate the same HTML as above by adding the asp-controller and asp-action attributes to an anchor tag as follows:

<a asp-controller="Account" 
asp-action="Register">Register</a>

In simple examples like the one above, there is very little difference between the tag helper approach and the _@Url.Action_ approach. The advantage of tag helpers over the @Html.ActionLink approach is that we can add html attributes or content to the a tag and those will be merged with the generated output. Adding attributes to the a tag was particularly clumsy with the @Html.ActionLink.

Adding Route Parameters

In some cases, you might need to specify additional parameters for the controller action that you are binding to. You can specify values for these parameters by adding attributes with the asp-route- prefix. For example, you might need to specify the id of a product you are linking to, you would add a asp-route-id attribute as follows:

<a asp-controller="Product" 
asp-action="Display"
asp-route-id="@ViewBag.ProductId">

View Details</a>

…would generate the following html when ViewBag.Id = 1:

<a href="/Product/Display/1">View Details</a>

You can specify values for as many parameters as you need using attributes with the asp-route- prefix.

Named Routes

Another option is to specify the controller and action using a named route. For example, if your application had a route named login defined as follows in your MVC route configuration:

routes.MapRoute(
name: "login",
template: "login",
defaults: new { controller = "Account", action = "Login" });

then you can bind an anchor tag to that route as follows:

<a asp-route="login">Login</a>

Protocol, Host and Fragment

The anchor tag helper also has options for specifying the protocol, host and fragment for the generated URL. These are useful if you want to override the defaults to force https or link to a particular portion of a page. To do this using the `@Html.ActionLink` approach, you would need to use the overload that accepts all possible options:

@Html.ActionLink("Register", "Register", "Account",  "https", "aspecificdomain.com", "fragment", null, null)

Notice that you need to specify null for the last 2 parameters (route values and html attributes). This would generate the following HTML:

<a href="https://aspecificdomain.com/Account/Register#fragment">Register</a>

If all you wanted to do was specify https for the current domain, you would need to pass null to 4 parameters:

@Html.ActionLink("Register", "Register", "Account",  "https", null, null, null, null)

With the tag helper approach, you can use any combination of the asp-protocol, _asp-host _and asp-fragment attributes you want. This avoids messing around with method overloads that contain parameters you don’t care about in the current context. The result is a much cleaner syntax that, in my opinion, is easier for the reader to understand:

<a asp-controller="Account" 
asp-action="Register"
asp-protocol="https"
asp-host="asepecificdomain.com"
asp-fragment="fragment">Register</a>


<!--or with the protocol only -->
<a asp-controller="Account"
asp-action="Register"
asp-protocol="https">Register</a>

Conclusion

That wraps up the functionality provided by the anchor tag helper in ASP.NET Core MVC. In the next post in this series we will explore the Cache tag helper.

ASP.NET Core MVC Form Tag Helper

So for in my series covering ASP.NET Core MVC tag helpers, I have covered all the tag helpers that you would use to generate the contents of a form:

In this post, I will cover the Form tag helper that is used to bind a Form element to a particular MVC controller action or named route. This tag helper is an alternative to the following HTML helper method syntax:

@using (Html.BeginForm("ControllerName", "ActionName"))
{
//Form Contents Here
}

Binding to Controller Action

With the form tag helper, you can bind a form to a specific controller action by adding the asp-controller and asp-action tag helper attributes to a form element.

For example, you can bind a form to the Login action of the Account controller as follows:

<form asp-controller="Account" 
asp-action="Login">

//Your form elements here
</form>

Any elements that you add inside the form will be included in the generated HTML. The example above would generate the following HTML:

<form action="/Account/Login" method="post">

//Your form elements here

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8AFtmUdx-b5MkQvAyGYbjFmMGSMv0Fmk7gG4RqGXlkNV6yqKqj6fgqnOh4TLT6ZnWSaqtAbKkgpEB20lvfkc2iOKZKIqt3tJ4Jij8DjmatTrZo-DKVOLwwOzj3kB8VKpFwc0rQMjaJTTC_gVv5f0vAg">
</form>

The form method was defaulted to post. You can specify this manually in your markup if you want. Any HTML attributes you add to the form element will be included in the generated HTML. However, if you attempt to both manually set the action attribute AND the asp-controller / asp-action attributes, the form tag helper will raise an error.

Cannot override the ‘action’ attribute for <form>. A <form> with a specified ‘action’ must not have attributes starting with ‘asp-route-‘ or an ‘asp-action’ or ‘asp-controller’ attribute.

The form tag helper also automatically adds an Anti forgery token which will cover in more detail below.

Adding Parameters

In some cases, you might need to specify additional parameters for the controller action that you are binding to. You can specify values for these parameters by adding attributes with the asp-route- prefix. For example, it is common for the Login action to have a returnUrl parameter. This can be specified as follows:

<form asp-controller="Account" 
asp-action="Login"
asp-route-returnurl="@ViewBag.ReturnUrl"
method="post" >

</form>

…which would generate a form element with the action attribute containing the specified value for the returnurl parameter:

<form action="/Account/Login?returnurl=%2FHome%2FAbout" method="post">

//Your form elements here

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8AFtmUdx-b5MkQvAyGYbjFmMGSMv0Fmk7gG4RqGXlkNV6yqKqj6fgqnOh4TLT6ZnWSaqtAbKkgpEB20lvfkc2iOKZKIqt3tJ4Jij8DjmatTrZo-DKVOLwwOzj3kB8VKpFwc0rQMjaJTTC_gVv5f0vAg">
</form>

You can specify values for as many parameters as you need using attributes with the asp-route- prefix.

Binding to a Named Route

Another option is to specify the controller and action using a named route. For example, if I had a route named login defined as follows in my MVC route configuration:

routes.MapRoute(
name: "login",
template: "login",
defaults: new { controller = "Account", action = "Login" });

Then I could bind my form to the login route using the asp-route tag helper attribute:

<form asp-route="login" 
asp-route-returnurl="@ViewBag.ReturnUrl"
method="post" >

</form>

…which would generate the following HTML:

<form action="/login?returnurl=%2FHome%2FAbout" method="post">

//Your form elements here

<input name="__RequestVerificationToken" type="hidden" value="CfDJ8AFtmUdx-b5MkQvAyGYbjFmMGSMv0Fmk7gG4RqGXlkNV6yqKqj6fgqnOh4TLT6ZnWSaqtAbKkgpEB20lvfkc2iOKZKIqt3tJ4Jij8DjmatTrZo-DKVOLwwOzj3kB8VKpFwc0rQMjaJTTC_gVv5f0vAg">
</form>

Anti-Forgery Token Option

As we saw earlier, the form tag helper attribute adds a hidden __RequestVerificationToken input. This hidden input contains an anti-forgery token that when used in combination with the [ValidateAntiForgeryToken] attribute on the controller action will help to protect your application against cross-site request forgery. You really should use this on all your forms but if for some reason you need to turn this off for a particular form, you can simply set the asp-anti-forgery token to false.

<form asp-controller="Account" 
asp-anti-forgery="false"
asp-action="Login">

//Your form elements here
</form>

Conclusion

That wraps up the functionality provided by the form tag helper in ASP.NET Core MVC. I think it provides a much cleaner syntax than the @using(Html.BeginForm()){…} syntax. What do you think? What will be your preferred syntax for forms in MVC?

ASP.NET Core MVC Select Tag Helper

In this post from my series exploring the ASP.NET Core MVC tag helpers, I will be covering the select tag helper.

The select tag helper is used to generated select and associated option elements for properties of your models. This is an alternative to the _Html.DropDownListFor _HTML helper method.

Select Tag Helper

The select tag helper is used by adding the asp-for attribute to a select element. For example consider a simple view model class containing a CountryCode property:

public class SimpleViewModel
{
public string CountryCode { get; set; }
}

You can bind a select element to the CountryCode property as follows:

<select asp-for="CountryCode"></select>

…which would generate the following HTML:

<select name="CountryCode" 
id="CountryCode">

</select>

Now this is not overly useful because it is an empty drowdown list. You will probably want to add some options to your select.

Adding options

There are a couple ways you can add options to the select. You can simply add options directly in the markup:

<select asp-for="CountryCode">
<option value="CA">Canada</option>
<option value="US">US</option>
<option value="--">Other</option>
</select>

In this case, the options specified in markup will be included in the generated HTML. The selected option will automatically be determined based on the value of the model property. For example, if the CountryCode property is set to ‘CA’ in the model, then the following HTML would be generated:

<select name="CountryCode" id="CountryCode">
<option selected="selected" value="CA">Canada</option>
<option value="US">US</option>
<option value="--">Other</option>
</select>

If the list of options is dynamically loaded from a data source, you can use the asp-items tag helper attribute. All you need to do is set the asp-items attribute to an IEnumerable. For example, if we had a list of countries available on the ViewBag, we could specify the select options as follows:

<select asp-for="CountryCode" 
asp-items="ViewBag.Countries">

</select>

This will generate a select with options for each of the SelectListItems in the Countries collection.

Multiple Select

The select tag helper will automatically generate a multi-select if the property specified in the asp-for attribute is an IEnumerable. For example, given the following model class:

public class SimpleViewModel
{
public IEnumerable<string> CountryCodes { get; set; }
}

Binding the asp-for attribute to the CountryCodes property as follows:

<select asp-for="CountryCodes" 
asp-items="ViewBag.Countries">

</select>

…would generate the following HTML:

<select name="CountryCodes" 
id="CountryCodes"
multiple="multiple">

<option selected="selected"
value="CA">Canada</option>

<option value="USA">United States</option>
<option value="--">Other</option>
</select>

Conclusion

That covers all the functionality provided by the select tag helper in ASP.NET Core MVC, which provides a clean syntax for generating select elements based on the values in your model. In the next post, I will cover the Form tag helper.

ASP.NET Core MVC TextArea Tag Helper

In this post from the ASP.NET Core MVC Tag Helper series I will be covering the textarea tag helper.

The textarea tag helper is very similar to the input tag helper but specifically targets the textarea element instead of the input element. The textarea tag helper is used by adding the_ asp-for_ tag helper attribute to a text area element.

Consider the following model class:

public class SimpleViewModel
{
    [Required]
    [MaxLength(5000)]
    public string Description { get; set; }
}

We can bind a textarea to the Description property as follows:

<textarea asp-for="Description"></textarea>

…which would generate the following HTML:

<textarea name="Description" id="Description" 
          data-val-required="The Description field is required." 
          data-val-maxlength-max="5000" 
          data-val-maxlength="The field Description must be a string or array type with a maximum length of '5000'." 
          data-val="true"></textarea>

The text area tag helper was able to generate name and _id_ properties based on the name of the property specified in asp-for. It also generated _data-val _attributes to enable client side validation based on the Required and MaxLength attributes that were applied to the model property.

Note: This tag helper is an alternative to the Html.TextAreaFor HTML helper method.

As with the other tag helpers, any HTML attributes that you add to the textarea element will be merged with the generated attributes. For example, adding the rows and cols attributes as follows:

<textarea asp-for="Description" rows="5" cols="40"></textarea>

…would generate the following HTML:

<textarea name="Description" id="Description" 
     rows="5" cols="40" 
     data-val-required="The Description field is required." 
     data-val-maxlength-max="5000" 
     data-val-maxlength="The field Description must be a string or array type with a maximum length of '5000'." 
     data-val="true"></textarea>

That covers everything you need to know about the textarea tag helper. In the next post in the series I will cover the Select tag helper.