tag-helpers

Using the _ViewImports.cshtml File to Set Up View Namespaces in MVC 6

As of Beta 5 of ASP.NET 5, there is a new file in the Views folder of an MVC project called _ViewImports.cshtml (here's the GitHub item). In previous Beta versions, this file was called _GlobalImport.cshtml; even though it has been renamed, its functionality is the same in Beta 5 as in Beta 3. What exactly does it do? Let's find out!

Purpose

_ViewImports.cshtml serves one major purpose: to provide namespaces which can be used by all other views. In previous MVC projects, this functionality was provided by the web.config file in the Views folder; since the web.config no longer exists, global namespaces are now provided by this file.

At any rate, a simple _ViewImports.cshtml file might look like this:

@using MyProject.Web
@using Microsoft.Framework.OptionsModel
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

The first two lines are fairly easy to understand: we want the MyProject.Web and Microsoft.Framework.OptionsModel namespaces to be available to all views. If we wanted another namespace to be available, we'd simply add it here.

The last line is a bit trickier. We've already seen examples of TagHelpers in previous posts but we've glossed over exactly how they get enabled; I want to remedy that in this post.

The reason that we use the @addTagHelper keyword is because we need the Tag Helpers to actually execute (at some point in the page rendering lifecycle; I'm not sure exactly where yet) rather than just be available to the views. So, we specify a tag helper namespace, and the asterisk (*) specifies that we want all tag helpers in that namespace to be used.

That's it! The _ViewImports.cshtml file is responsible for setting up namespaces that can be accessed by the views in your MVC 6 project, which was previously done by the web.config file in the views folder. IMO this solution is much easier to read, but you should go make up your own mind. Now go out there and start using ASP.NET 5 and MVC 6; trust me, you'll be glad you did.

Happy Coding!

An Overview of Tag Helpers in ASP.NET Core

I was a little hesitant about the new Tag Helpers feature coming out with ASP.NET Core and ASP.NET MVC 6 when I last wrote about it, but a few recent updates have totally changed my mind. These things are going to be awesome.

Are you still trying to understand what tag helpers are, and why we'd use them over good old Razor helpers? Then read on!

Here's the sections in this tutorial:

What's a Tag Helper?

It's a set of code that allows you to no longer have to use Razor helpers to build your cshtml forms. What this means is that when you had to write this:

@Html.ActionLink("Add a Movie", "Add", "Movie")

You can now write this:

<a asp-action="Add" asp-controller="Movie">Add a Movie</a>  

Because of this, you can now write views that look like HTML rather than an unwieldy mix of HTML and C#.

The Completed Form

I like to show the results first. Here's the view model we will be using:

public class AddMovieVM  
{
    [Required(ErrorMessage = "Please enter a title for this movie.")]
    [Display(Name = "Title:")]
    public string Title { get; set; }

    [Required(ErrorMessage = "Please select a release date for this movie.")]
    [Display(Name = "Release Date:")]
    public DateTime ReleaseDate { get; set; }

    [Required(ErrorMessage = "Please enter a running time for this movie")]
    [Range(1, 1000, ErrorMessage = "The running time must be between 1 and 1000 minutes.")]
    [Display(Name = "Running Time:")]
    public int RunningTime { get; set; }

    [Display(Name = "Description:")]
    public string Description { get; set; }

    [Display(Name = "Genre:")]
    public int SelectedGenre { get; set; }

    public List<SelectListItem> AllGenres { get; set; }
}

Now, if we were using HTML Helpers, our corresponding Add view might look like this:

@model MoreTagHelpers.ViewModels.AddMovieVM

<h2>Add a Movie</h2>  
@Html.ActionLink("Back to Movies", "Add", "Movie")
@using (Html.BeginForm("Add", "Movie"))
{
    <div>
        <div>
            @Html.LabelFor(x=>x.Title)
            @Html.TextBoxFor(x=>x.Title)
            @Html.ValidationMessageFor(x=>x.Title)
        </div>
        <div>
            @Html.LabelFor(x => x.ReleaseDate)
            @Html.TextBoxFor(x => x.ReleaseDate, new { @type = "date" })
            @Html.ValidationMessageFor(x => x.ReleaseDate)
        </div>
        <div>
            @Html.LabelFor(x => x.RunningTime)
            @Html.TextBoxFor(x => x.RunningTime, new { @type = "number" })
            @Html.ValidationMessageFor(x => x.RunningTime)
        </div>
        <div>
            @Html.LabelFor(x => x.Description)
            @Html.TextAreaFor(x => x.Description)
            @Html.ValidationMessageFor(x => x.Description)
        </div>
        <div>
            @Html.LabelFor(x=>x.SelectedGenre)
            @Html.DropDownListFor(x => x.SelectedGenre, Model.AllGenres)
            @Html.ValidationMessageFor(x => x.SelectedGenre)
        </div>
        <div>
            <input type="submit" value="Save" />
        </div>
    </div>
}

Now, here's that same form, using Tag Helpers.

@model MoreTagHelpers.ViewModels.AddMovieVM

<h2>Add a Movie</h2>  
<a asp-action="Index" asp-controller="Movie">Back to Movies</a>  
<form asp-action="Add" asp-anti-forgery="true" asp-controller="Movie">  
    <div>
        <div>
            <label asp-for="Title"></label>
            <input type="text" asp-for="Title" />
            <span asp-validation-for="Title"></span>
        </div>
        <div>
            <label asp-for="ReleaseDate"></label>
            <input type="date" asp-for="ReleaseDate" />
            <span asp-validation-for="ReleaseDate"></span>
        </div>
        <div>
            <label asp-for="RunningTime"></label>
            <input type="number" asp-for="RunningTime" />
            <span asp-validation-for="RunningTime"></span>
        </div>
        <div>
            <label asp-for="Description"></label>
            <textarea asp-for="Description"></textarea>
            <span asp-validation-for="Description"></span>
        </div>
        <div>
            <label asp-for="SelectedGenre"></label>
            <select asp-for="SelectedGenre" asp-items="Model.AllGenres"></select>
            <span asp-validation-for="SelectedGenre"></span>
        </div>
        <div>
            <input type="submit" value="Save" />
        </div>
    </div>
</form>  

HTML all the things! That second form is much cleaner than the first IMHO. But, what really makes it is that Visual Studio will highlight which HTML tags are using tag helpers:

The normal HTML is highlighted regularly, and the tag helper HTML is in purple, making it really obvious which is which and hopefully negating some of the complaints I've seen about Razor being more obvious than Tag Helpers.

Let's dive into some of these helpers to see how you can use them in your ASP.NET MVC 6 projects.

Anchor Tag Helper

First, let's look at this helper:

<a asp-action="Index" asp-controller="Movie">Back to Movies</a>  

That's just an anchor tag with two tag helper attributes. Notice that each tag helper attribute begins with "asp-", and their names make intrinsic sense to us: "action" and "controller".

In the particular case of the anchor tag helper, there's a few other properties we could set, like asp-fragment, asp-route, and asp-path. Even better, Visual Studio now gives you intellisense for these attributes:

Form Tag Helper

The form tag helper looks something like this:

<form asp-action="Add" asp-anti-forgery="true" asp-controller="Movie"></form>  

The properties "asp-action" and "asp-controller" are same as in the anchor tag helper, but notice the "asp-anti-forgery" property. Setting this to true is the same as using the @Html.AntiForgeryToken helper in Razor (which you should always be using). This helper has simply made your views a little bit cleaner.

Label Tag Helper

Making your views a little bit cleaner is a theme you'll see woven into each of the tag helpers, and the next example comes with the label tag helper. Here's our example:

<label asp-for="Title"></label>  

That's really simple, in more ways than one. Note that there's no more lambda expressions (no more x => x.Title) and no more mixed-in C#. Plus, we can now wrap elements inside of the label without needing to create custom helpers.

Input Tag Helper

Continuing with the simple theme:

<input type="text" asp-for="Title" />  

Now there are many different kinds of input tags (e.g. date, checkbox, text, radio, etc.) and each kind had a different Razor helper. In the new tag helpers, there's just the input tag helper, with two attributes: "asp-for" and "asp-format".

TextArea Tag Helper

The text area is a separate tag in HTML (which I probably should have known before I started researching this blog post, but oh well). Here's the helper for it:

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

Select Tag Helper

Another tag which is different from the standard input tag is the select tag, and consequently it has a different tag helper:

<select asp-for="SelectedGenre" asp-items="Model.AllGenres"></select>  

Again we see "asp-for" but now we also see the source collection specified using "asp-items." Easy. One odd thing I noticed about this is that "asp-for" doesn't require the Model. prefix, but "asp-items" does. If anybody knows why this is, I'd love to hear it.

Validation Tag Helper

The final tag helper we see on the completed form above is the one on a <span> tag. This is an unusual one, since under many circumstances you won't want to use a span tag as anything other than a <span>.

Except when what you want is a validation message. In that case, you'd use this:

<span asp-validation-for="Description"></span>  

This is the time when we should show the HTML that this renders:

<span class="field-validation-valid" data-valmsg-for="SelectedGenre" data-valmsg-replace="true"></span>  

Well well well, that looks familiar. This renders the same HTML as the Razor helpers, so it's really a no-lose situation for us web developers.

But wait, that's not all! There are several other helpers we can use.

Environment Tag Helper

Here's one of those other helpers:

<environment names="Development">  
...
</environment>  
<environment names="Staging,Production">  
...
</environment>  

This helper is really interesting because the contents of the helper only get rendered if the HTML is deployed into specific environments. It does this by looking at an environment variable called ASPNET_ENV, and which you can set by right-clicking on the Project file and selecting Properties, then selecting Debug.

I'm hoping there's an easier way to change this variable in the fully-released version of Visual Studio, but until then, this is how you use it.

Link and Script Tag Helpers

These are possibly the most interesting new helpers.

<link rel="stylesheet" href="//ajax.aspnetcdn.com/ajax/bootstrap/3.0.0/css/bootstrap.min.css"  
asp-fallback-href="~/lib/bootstrap/css/bootstrap.min.css" asp-fallback-test-class="hidden" asp-fallback-test-property="visibility" asp-fallback-test-value="hidden" />

<script src="//ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.js" asp-fallback-src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js" asp-fallback-test="window.jQuery"></script>  

There's a lot going on here. First, notice the values in the src attribute. Those urls point to the Microsoft Ajax CDN, a new (to me, at least) content-delivery network that holds many commonly-used production CSS and JS files, particularly those produced and maintained by Microsoft. In our case, in the sample project we get the script files for jQuery, unobtrusive validation, bootstrap, and others.

But what about those "asp-fallback-src" attributes? They specify a second location that we can go grab the source files from should the first location be unavailable. In other words, we now have a fallback plan that allows our CSS and JS files to gracefully degrade over should the CDN (if we are using one) not be available. Finally, note that the fallback parameters are entirely optional.

Image Tag Helper

New in the Beta 5 release of ASP.NET is the Image Tag Helper which looks like this:

<img src="~/content/images/profile.jpg"  
     alt="John Smith" 
     asp-append-version="true" />

Notice the new asp-append-version attribute; that's the only TagHelper attribute in this tag. What that does is render an <img> with a query string attribute appended on the source URL, like this:

<img src="/content/images/profile.jpg?v=X5q6D366_nQ2fQqUso0F24gWy2ZekXjHz83KmWyaiOOk"  
     alt="company logo"/>

The content of the v attribute is determined by the content of the image; if the content of the image changes, so does the v attribute. This forces the browser to download the image again.

This is a technique known as cache busting; writing code that forces the browser to download a resource (in this case, an image) if that resource changes. This technique guarantees that the user will always get the most recent version of the resource.

Cache Tag Helper

The Cache tag helper is similar to the Environment tag helper in that it doesn't target a specific HTML tag. Rather, it caches its contents in server-side memory, set to an expiration date. An example might look like this:

<cache expires-after="@TimeSpan.FromMinutes(5)" vary-by-user="true">  
    @Html.Partial("WeatherReport")
</cache>  

In this sample, the WeatherReport partial view will be cached for 5 minutes, and will vary by the user. We can do this to significantly reduce load on the server as all of these requests will be cached server-side.

What actually happens is that ASP.NET generates an ID that is unique to the context of the cached content, which allows you to have multiple <cache> on a single page and not have them interfere with one another.

There are quite a few ways to control how the cache operates. Attributes include expires-after, expires-on, expires-sliding, vary-by-user, vary-by-query, vary-by-route, etc. You can use any combination of vary-by-* attributes on a single cache tag, and doing so modifies the ID that is generated for the cache.

There's one major limitation to this tag: on the back end, it uses a construct called MemoryCache to store the cached content. MemoryCache is limited by the amount of memory available; once there's not enough memory to go around, MemoryCache will purge items until it has the memory necessary. Further, any app pool reset or server shutdown will destroy this cache. Basically, don't treat MemoryCache like a persistent storage solution, because it is not such a thing.

For more info, see Dave Paquette's wonderful in-depth article on the Cache Tag Helper.

Summary

All of this is to say that Tag Helpers are a significant step forward in coding efficiency for ASP.NET MVC and Visual Studio. Go grab the latest version of Visual Studio (and check out the sample project) to get started on using these things!

Happy Coding!

Tag Helpers in ASP.NET 5 Beta 3

NOTE: I have written a more updated tutorial on Tag Helpers. Check it out!

I mentioned in Welcoming the Coming Death of WebForms that one of the features I was most excited about in ASP.NET 5 was Tag Helpers. I've finally gotten some time to install VS 2015 CTP 6 and try this feature out, and I'm pretty impressed by these early examples.

Why use Tag Helpers?

Because it gets us closer to the ideal webpage that's written only in HTML and Javascript. Tag Helpers are a way of removing clunky C# code from our Razor views and making them look more like straight HTML. It also assists non-programmers (e.g. designers) who may not be familiar with the Razor HtmlHelpers in continuing to design the web pages they need to handle.

A Simple Example

Here's a sample form, written in MVC5 Razor using HtmlHelpers:

@Html.ActionLink("Back to Users", "Index", new { @class = "back-link" } )

@using(Html.BeginForm("Add", "User", FormMethod.Post), new { @class = "myform" })
{
    <div>
        @Html.AntiForgeryToken()
        @Html.ValidationSummary()
        <div>
            @Html.LabelFor(x => x.FirstName)
            @Html.TextBoxFor(x => x.FirstName, new { @class = "name_input" })
            @Html.ValidationMessageFor(x => x.FirstName)
        </div>
        <div>
            @Html.LabelFor(x => x.LastName)
            @Html.TextBoxFor(x => x.LastName, new { @class = "name_input" })
            @Html.ValidationMessageFor(x => x.LastName)
        </div>
        <div>
            @Html.LabelFor(x => x.DateOfBirth)
            @Html.EditorFor(x => x.DateOfBirth)
            @Html.ValidationMessageFor(x => x.DateOfBirth)
        </div>
        <div>
            <input type="submit" value="Go!" />
        </div>
    </div>
}

There's a surprising amount of C# code in that example, and a lot of calls to @Html. It's almost like we are approximating using HTML using C#.

Maybe instead of approximating HTML, we can make our markup look like actual HTML.

Here's that same form, written using TagHelpers:

<a asp-action="Add" class="back-link">Back to Users</a>

<form asp-controller="User" asp-action="Add" method="post" asp-anti-forgery="true">  
    <div>
        <div asp-validation-summary="ValidationSummary.ModelOnly">
        <div>
            <label asp-for="FirstName"></label>
            <input asp-for="FirstName" class="name_input" />
            <span asp-validation-for="FirstName"></span>
        </div>
        <div>
            <label asp-for="LastName"></label>
            <input asp-for="LastName" class="name_input" />
            <span asp-validation-for="LastName"></span>
        </div>
        <div>
            <label asp-for="DateOfBirth"></label>
            <input asp-for="DateOfBirth" />
            <span asp-validation-for="DateOfBirth"></span>
        </div>
        <div>
            <input type="submit" value="Go!" />
        </div>
    </div>
</form>  

That looks a lot like regular HTML, and IMHO looks quite a bit nicer than the Razor/C# syntax.

What are all those "asp-" attributes? They are how Tag Helpers work; they add server-side attributes to HTML elements without needing calls to C#.

Take a look at the <form> for example. It has three non-HTML attributes:

  • asp-controller: Specifies the name of the controller
  • asp-action: Specifies the name of the action
  • asp-anti-forgery: Specifies whether this form should be using AntiForgeryTokens (which you should always use)

Also note that Tag Helpers still follow the convention-over-configuration ideal that's omnipresent in MVC: the anchor tag only needs asp-action, not asp-controller, because it assumes there's an action specified in the present controller (which is the same assumption made by some overloads of Html.ActionLink).

Those attributes don't get rendered to the page; rather, the new Razor engine processes them and creates the HTML attributes (or, in the case of asp-anti-forgery, the HTML element) specified by these psuedo-attributes.

Some of you might be wondering that this looks kind of similar to WebForms outputs, where the attributes of the <asp:> controls didn't necessarily map one-to-one with actual HTML attributes. This is not the case here, there's no control lifecycle or anything being implemented by these helpers. Tag Helpers are just a nice compact way of showing what you want that element to do server-side.

How Does This Actually Work?

What is actually happening here is that ASP.NET is taking the HTML code from the Razor view and decoding it using a set of TagHelpers (GitHub source). There is a tag helper for each kind of tag that can use these attributes, and the code for them is actually rather simple; I highly encourage you to peruse that source.

Let's take, for example, the AnchorTagHelper.
When the rendering engine encounters an anchor tag on a CSHTML view, the content of that tag is fed through AnchorTagHelper, with each attribute checked to see if it matches one of the possible server-side attributes. If any of the Tag Helper attributes are found, they are run through a converter that ultimately produces the correct HTML for the specified Attributes. Note that, in the specific case of AnchorTagHelper, an exception is thrown if both href and controller or action exist, since the anchor won't be able to decide which one to use.

Important Notes on Tag Helpers

There is some setup that you need to do in order to make Tag Helpers work. TagHelpers is a NuGet package so you'll have to install that. You will also have to specify what DLL will be handling implementing the helpers using the @addtaghelper Razor syntax (I placed this in the _ViewStart view):

@addtaghelper "Microsoft.AspNet.Mvc.TagHelpers"

Second, the "asp-" prefix is optional, and actually is only supported starting in beta 3. I'm using the prefix above because I personally like being able to immediately distinguish between attributes that invoke server-side code and regular HTML attributes, but you can use them either with or without the prefix.

All in all, I'm impressed with this idea and its execution, and am looking forward to VS 2015 and ASP.NET 5 so I can start using this in my projects.

Check out Damian Edwards' TagHelperStarterWeb project on GitHub, read Scott Hanselman's blog post, and watch the TagHelpers demo for more examples.

Happy Coding!