Custom Templates, Data Annotations and UI Hints in ASP.NET MVC

This article is from our ASP.NET MVC 101 Tutorial Series

Today we look at a feature in ASP.NET MVC that is not really new but immensely useful if we are building ASP.NET MVC applications that lean on the server to do the UI rendering rather than using JavaScript frameworks like KnockoutJS.

Display Templates conceptually are tied to how a particular rendering engine (Razor or Webforms etc.) determines the Type of the model element it is trying to render and then using that information, pick up the Display Template that needs to be used. Along with type, other attributes like Data Annotations and UI Hints are also used to determine the final outcome of our UI.

A Simple ASP.NET MVC Demo

Let’s look at these concepts in a little more details using a simple demo app.


- Say we start off with the Internet Application ASP.NET MVC4 Project

internet-template

- Add an Entity in the Model folder with the following properties

public class TimeCard
{
    public int Id { get; set; }
    public string Subject { get; set; }
    public string Description { get; set; }
    public DateTime? StartDate { get; set; }
    public Decimal NumberOfHours { get; set; }
}

- Use the Add Controller method to scaffold up the Controller and Views for it.

default-controller

If we run the Application now and Navigate to the Create page for the DefaultController, it looks like this

default-create-page

Now we will go back to the TimeCard entity and decorate it with attributes as follows

public class TimeCard
{
    public int Id { get; set; }
    public string Subject { get; set; }
    [DataType(DataType.MultilineText)]
    public string Description { get; set; }
    [DisplayName("Start Date")]
    public DateTime? StartDate { get; set; }
    [DataType(DataType.Duration)]
    [DisplayName("Number of Hours")]
    public Decimal NumberOfHours { get; set; }
}


When we run the application again, the view changes to the following.

with-display-attributes-create-page

As we can see, the Description box is now much bigger thanks to the DataType annotation, and the labels look better thanks to the DisplayName attribute. However we still don’t have any DatePicker for our Dates, and what if we wanted the Number of hours to be a select going from 1 to 24?
We could always go ahead and edit the view and cram in the required jQuery to put in a Date Picker. Similarly we could remove the default text box that our MVC scaffolding is generating and replace it with a custom “Select”. Or, there is another way around it – Custom Templates.

Custom Templates in ASP.NET MVC

Universal Template

Let’s see how we can use Custom Templates to replace the Start Date with a Date Picker. By universal template, I mean the changes that we are going to do is going to apply to the entire project.
- In the Shared folder, we’ll add a folder called EditorTemplates

- Next we add a CSHTML file called DateTime.cshtml and add the following content to it

@model DateTime?
@Html.TextBox("", (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty), new { @class = "datepicker" })

- As we can see, it simply drops in a TextBox with a CSS class called datepicker. This magically doesn’t convert it into a date picker. To tie it up with a date picker JavaScript control, you need to do a couple of additional steps. First thing, add a new JavaScript file in the Scripts folder and call it jquery.ext.datepicker.js

image

- In the JS file we drop the following JavaScript

$(function ()
{
    $(".datepicker").datepicker();
});


- Now we’ve to include it in one of our JavaScript bundles. Let’s update the BundleConfig.cs and add it to the jquery bundles as follows

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*",
"~/Scripts/jquery.ext*"));


- We are all set. Note we have not touched any of the cshtml files. Let’s run the application. The create page will be as follows
date-picker-display-template

- If you save the entry and navigate back to it for Editing, you’ll see that the Edit page also has a date picker.

default-edit-page

- Fact is, here on when we use the Html.EditorFor(…) any DateTime type of field, it will get the date picker for free.

Reducing Scope of the Editor

Now if we wanted to reduce the scope of the Template, we could very well define it under Views\Default\EditorTemplates. This would have restricted it to the view in the Default controller.

Inverting the Template Association

Now that we’ve seen how to do a Universal Template and a Template per controller, let’s see what it takes to create a template that will be used only if we want to use it.

- Let’s add another cshtml file under the EditorTemplates folder and call it HoursOfTheDay.cshtml
- Add the following markup to it

@model Decimal?
<select>
    <option value="0">Please Select</option>
    @for (int i = 1; i <= 24; i++)
    {
        if (Model.HasValue && ((int)Model.Value) == i)
        {
            <option value="@i" selected="selected">@i</option>
        }
        else
        {
            <option value="@i">@i</option>
        }
    }
</select>


- What this does is for Decimal values it provides a drop down with items 1 to 24. It also checks if the input value is between 1 – 24 and if it is, sets it as the selected option

- Now since it has a name HoursOfTheDay it will not get bound to all Decimal fields automatically. Instead there are two options to bind it.

Option 1: Using the UIHint attribute on our Model as follows

[DataType(DataType.Duration)]
[DisplayName("Number of Hours")]
[UIHint("HoursOfTheDay")]
public Decimal NumberOfHours { get; set; }


As we can see, the UIHint provides the name of the Template to use. Again this makes it applicable to all pages to which this Entity Type is bound.

template-using-uihint

Option 2: If we want even finer grained control over when the template should be used, we can remove the UIHint from the attribute and use it in the cshtml markup as follows

<div class="editor-field">
    @Html.EditorFor(model => model.NumberOfHours, "HoursOfTheDay")
    @Html.ValidationMessageFor(model => model.NumberOfHours)
</div>


Conclusion

With that we wrap up this peek into Custom Templates. We looked at Editor Templates specifically and how to use them either universally or in a fine grained manner.

We can also use Template for managing the Display only. These go under the folder DisplayTemplates. For example we could use a Display template to show all datetime fields as short dates only.

Download the entire source code of this article (Github)

3 comments:

  1. This time there is no more features in ASP.NET MVC but some of are good like bundling and minification and because of this I don't have to use more data for visitors of my site. and generally this features more liked by developers.

    ReplyDelete
  2. Clear article and most helpfull. Thank you.

    As a newbie in MVC/Razor I struggled a bit because I didn't include the JQueryUI stuff in the bundles. This caused object doesn't support errors.

    I found this article helpfull:-

    http://stackoverflow.com/questions/20081328/how-to-add-jqueryui-library-in-mvc-5-project

    ReplyDelete