Apr 26

MVC 2 is official, I’ve been using it for a while now (since preview), my favorite feature by far is templating. I’ve been using the templating feature extensively to upgrade an existing production application, in particular some quite ugly views.

Before templates to create a reusable UI, you really only had the options of user controls or writing your own Html helpers, the former will almost certainly mean a compromise. I believe the templating feature will assist in the goals of separation of concerns and being DRY.

 

Who should read this post

This post is aimed at people who have experience of MVC, but want to find out more about the templating feature of MVC 2. You will need to understand strongly typed Html helpers (passing your view data property to a Html helper as Lambda expression). If you don’t know what strongly typed Html helpers are I recommend you read Scott Gu’s excellent post first.

The Basics

Download the code for this example

Consistent with other strongly typed Html helpers Html.TextBox(model=>model.foo) etc, the syntax for loading custom templates is exactly the same as any other Html helper.

Like views the folder structure can be used to determine what template should be used. This allows you to use the built in templates first, then when required customise using the same syntax. Templates come in two flavors Editor and Display.

So how easy it is to use templates?

We are going to create a very simple MVC application that posts a Employee Model and then displays the model the user submitted. We are going to use templates to reduce the amount of mark-up in the two views.

Lets start by creating a new MVC ASP.NET Web Application (File -> Project -> Web -> MVC ASP.NET Web Application).

Then lets code a very simple model representing an Employee:

public class EmployeeModel {
	public string FirstName { get; set; }
	public string LastName { get; set; }
	public DateTime? DateOfBirth { get; set; }
}

The simplest of Controllers:

[HandleError]
public class EmployeeController : Controller {
	[HttpGet]
	public ActionResult Index() {
		return View();
	}
}
Add View to the Employee Controller

Add a View to the Employee Controller

And lastly lets add view for the Employee\Index action, this will be a strongly typed View for a “Create” scenario.

The quickest and easiest way to do this is by creating a “Employee” folder underneath /views, then add a view by right mouse clicking on the “Employee” folder and selecting “Add View”, when presented with the “Add View” dialog, do the following:

  • Name the view “Index”
  • Check “Create a strongly-typed view”
  • Select the Employee model as the view data class
  • Select “Create” as your View content
Add /Employee/Index View Dialog

Add /Employee/Index View Dialog

This will generate the View below:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>



	Index




    

Index

<% using (Html.BeginForm()) {%> <%= Html.ValidationSummary(true) %>
Fields
<%= Html.LabelFor(model => model.FirstName) %>
<%= Html.TextBoxFor(model => model.FirstName) %> <%= Html.ValidationMessageFor(model => model.FirstName) %>
<%= Html.LabelFor(model => model.LastName) %>
<%= Html.TextBoxFor(model => model.LastName) %> <%= Html.ValidationMessageFor(model => model.LastName) %>
<%= Html.LabelFor(model => model.DateOfBirth) %>
<%= Html.TextBoxFor(model => model.DateOfBirth) %> <%= Html.ValidationMessageFor(model => model.DateOfBirth) %>

<% } %>
<%= Html.ActionLink("Back to List", "Index") %>

Debugging /Employee/Index you should see the following:

\Employee\Index

\Employee\Index

To use the built-in templates just replace the Html.TextBoxFor helper with the Html.EditorFor helper, the mark-up will look like this:


<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>


	Index




    

Index

<% using (Html.BeginForm()) {%> <%= Html.ValidationSummary(true) %>
Fields
<%= Html.LabelFor(model => model.FirstName) %>
<%= Html.DisplayFor(model => model.FirstName) %> <%= Html.ValidationMessageFor(model => model.FirstName) %>
<%= Html.LabelFor(model => model.LastName) %>
<%= Html.DisplayFor(model => model.LastName) %> <%= Html.ValidationMessageFor(model => model.LastName) %>
<%= Html.LabelFor(model => model.DateOfBirth) %>
<%= Html.DisplayFor(model => model.DateOfBirth) %> <%= Html.ValidationMessageFor(model => model.DateOfBirth) %>

<% } %>
<%= Html.ActionLink("Back to List", "Index") %>

Now debugging /Employee/Index you will notice that the view looks exactly the same, however for each of the view data’s property types you are now calling the default Editor template, it just so happens for DateTime and String it’s a Textbox. Next we’ll add our own template.

Add Shared Editor Template

Add Shared Editor Template

Adding Your Own Editor Template

Download the code for this example

For editing view data properties that have the type of string, we are going to add our own Shared Editor template.

Adding your own editor template is simple, create a folder underneath /Views/Shared called EditorTemplates, then add a view by right mouse clicking on the EditorTemplates folder and selecting “Add View”, when presented with the “Add View” dialog, do the following:

  • Name the template “string”
  • Check “Create a partial view”
  • Check “Create a strongly-typed view”
  • Type in “string” as the view data class
Add string template dialog
Add string template dialog

Paste the following code into your template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>

<%= Html.LabelFor(model => model) %>
<%= Html.TextBoxFor(model => model) %> <%= Html.ValidationMessageFor(model => model) %>

As you can see, we have refactored our view into a template, so that the Label, Textbox and Validation Message are all part of the one template. To make the passed in view data strongly-typed, you will notice the familiar use of ViewUserControl<string>. View data passed to a strongly-typed template is the model for that template (a sort of mini-model), therefore you can work with it like any other model. The other piece of the jigsaw is TemplateInfo, but I will discuss that later on in the post.

Now we need to remove the redundant markup from the view, change the /Views/Employee/Index view as follows:


<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>

	Index




    

Index

<% using (Html.BeginForm("Details","Employee")) {%> <%= Html.ValidationSummary(true) %>
Fields <%= Html.EditorFor(model => model.FirstName) %> <%= Html.EditorFor(model => model.LastName) %> <%= Html.EditorFor(model => model.DateOfBirth) %>

<% } %>
<%= Html.ActionLink("Back to List", "Index") %>

As you can see we followed the DRY principle (Don’t repeat yourself) with our view and as a result reduced the amount of code by about 50%.

Debugging /Employee/Index you will notice that the view looks exactly the same (sorry re-occurring theme in this post), but now you are calling your own Editor template for any of your view data properties that have a type of string.

How does this work?

The only significant difference between views and templates is that rather than naming the template after a action, we name it after the simple name of the target type, the MVC framework takes care of searching for matching templates. When calling the Editor template in our example Html.EditorFor(model => model.FirstName), the view data passed to the template is the type of and value of the model property selected by the Lambda expression.

As previously mentioned templates work in a similar way to views, the folder structure is used to determine what template should used, so if the template is controller specific you create a folder called EditorTemplates underneath Views/ControllerName (in our example Views/Employees) or under “Shared”, if you want the template available to all views. When a view calls a template it will look in Views/ControllerName/EditorTemplates first then /Shared/EditorTemplates. As seen in our first example if the template isn’t found rather than throwing an error, it will just default to the template for that type.

Limitations?

A current limitation around templates is complex types, for example if our Employee model had a property called Offices that had a type of List<Office>, because the MVC framework uses the type’s simple name to select your template you would have call it it List`1.ascx, not ideal as the template will be selected for any List<T>, there are some good workarounds for this limitation that I will discuss later on in the post.

Display Templates

Download the code for this example

I haven’t really mentioned Display Templates, Display Templates work in exactly the same way as Editor Templates, except they’re intended for displaying view data 🙂 For reference, below is how to create a Display Template, but the steps are pretty much the same, so I wouldn’t be offended if you skipped this section.

First lets add the following action to our Employee controller:

[HttpPost]
public ActionResult Details(EmployeeModel model)
{
	return View(model);
}
Add View to the Employee Controller

Add View to the Employee Controller

Then lets add a view for the Employee\Details action this will be a strongly-typed view for a “Details” scenario.

The quickest and easiest way to do this is by right mouse clicking on the “Employee” folder and selecting “Add View”, when presented with the “Add View” dialog, do the following:

  • Name the view “Details”
  • Check “Create a strongly-typed view”
  • Select the Employee model as the view data class
  • Select “Details” as your View content
Add /Employee/Details View Dialog

Add /Employee/Details View Dialog

This will generate the View below:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>

	Details




    

Details

Fields
FirstName
<%= Html.Encode(Model.FirstName) %>
LastName
<%= Html.Encode(Model.LastName) %>
DateOfBirth
<%= Html.Encode(String.Format("{0:g}", Model.DateOfBirth)) %>

<%= Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> | <%= Html.ActionLink("Back to List", "Index") %>

Now we need to change the Index view so it posts to our new Details action:

using (Html.BeginForm())

To:

using (Html.BeginForm("Details","Employee"))

Debug \Employee\Index fill out the form and click on the create button, you should see something similar to:

\Employee\Details

\Employee\Details

Let’s change the Details view to use the built-in templates by replacing Html.Encode helper with Html.DisplayFor helper, the mark-up will look like this:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>

	Details




    

Details

Fields
FirstName
<%= Html.DisplayFor(model => model.FirstName) %>
LastName
<%= Html.DisplayFor(model => mModel.LastName) %>
DateOfBirth
<%= Html.DisplayFor(model => model.DateOfBirth)) %>

<%= Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> | <%= Html.ActionLink("Back to List", "Index") %>

If you debug \Employee\Index and submit the form again, just like the EditorTemplate example, you will notice that the view looks exactly the same, however you are now calling the default DisplayTemplate for each of the types.

Adding Your Own Display Template

We now have a requirement to make each of labels for the Details view bold, we are also going to use the opportunity to refactor the view. To do this we are going to create a Shared template for handling properties that are type of string.

Add Display Template

Add Display Template

To add a display template create a folder underneath /Views/Shared called DisplayTemplates, then add a view by right mouse clicking on the folder and selecting “Add View”, when presented with the Add View dialog, do the following:

  • Name the template “string”
  • Check “Create a partial view”
  • Check “Create a strongly-typed view”
  • Type in “string” as the view data class
Add string template dialog

Add string template dialog

Paste the following code into your template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>

<%=Html.LabelFor(model => model)%>
<%=Html.Encode(Model)%>
Add DateTime template dialog

Add DateTime template dialog

We have another requirement to show the Date of Birth in long format, so lets create a Shared template for displaying DateTime view data properties.

Add the template in the same way you did for the string template, except use DateTime for the View name and View data class.

Paste the following code into your template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>"%>


<%=Html.LabelFor(model => model)%>
<%=Html.Encode(Model.HasValue ? Model.Value.ToLongDateString() : string.Empty)%>

Remove the redundant markup from the view, change the /Views/Employee/Details view as follows:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>

	Details




    

Details

Fields <%=Html.DisplayFor(model => model.FirstName)%> <%=Html.DisplayFor(model => model.LastName)%> <%=Html.DisplayFor(model => model.DateOfBirth)%>

<%= Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> | <%= Html.ActionLink("Back to List", "Index") %>

Debug\Employee\Index, fill out the form and click on the create button, you can now see that our view includes our new requirements for the DateTime and string types.

In the same way we did for the Index view, using templates we stuck to the DRY principle and reduced the code in the Details view significantly. Our Display templates work in exactly the same way as our Editor template, except we created the Display templates in the DisplayTemplates folder.

\Employee\Details

\Employee\Details

More Useful Example − DateTime Editor Template Recipe

Download the code for this example

I thought I’ll post a more useful example that you can use in your own projects. In this example we have a requirement to make it easier for the user to select a date. To meet this requirement we are going to create a date picker using an editor template and jQuery UI.

First of all you need to download jQuery UI, take all the defaults, but select the Redmond theme (you could of course select any theme, just change the code in the master page accordingly).

jQuery Download

Once downloaded, copy the \development-bundle\ui folder to the Scripts folder in your project. In your project under \content create a folder called jqueryui, then copy the \css\redmond folder to the \content\jqueryui folder. Ensure that the folders you copied are included in your project.

jQuery UI redmond theme

jQuery UI Redmond theme

jQuery UI

jQuery UI scripts

Change the head of the \Views\Shared\Site.Master master page to reference the jQuery UI scripts and theme CSS, the mark-up will look like this:


    <asp:ContentPlaceHolder ID="TitleContent" runat="server" />
     
    
    
    
    
    
    

Add Shared Editor Template

Add Shared Editor Template

Next lets add our own Shared Editor template, for editing view data properties that are type of DateTime.

Add a view by right mouse clicking on the Views\Shared\Editor template folder and select “Add View”, when presented with the “Add View” dialog, do the following:

  • Name the template “DateTime”
  • Check “Create a partial view”
  • Check “Create a strongly-typed view”
  • Type in “DateTime” as the view data class
Add DateTime template dialog

Add DateTime template dialog

Paste the following code into your template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>

<%string name = ViewData.TemplateInfo.HtmlFieldPrefix;%>
<%string id = name.Replace(".", "_");%>

<%= Html.LabelFor(model => model) %>
<%= Html.TextBoxFor(model => model) %> <%= Html.ValidationMessageFor(model => model) %>

To see the date picker in action, debug\Employee\Index and click the “…” button next to the DateOfBirth textbox.

\Employee\Index date picker

\Employee\Index date picker

This shared template is a completely reusable and will work for any view data that is type of DateTime, all with < 25 lines of code.

How it Works − Introducing TemplateInfo

So how did this work, in terms of how the template is selected using the Html.EditorFor helper, nothing has changed from how our string Editor template worked. However to make use of jQuery UI and to make the template reusable, we need a way to know what element id will be created by the Html.TextBox helper. This is where TemplateInfo comes in, TemplateInfo is described as: “The TemplateInfo class is used to retrieve metadata for templates or HTML helpers that you create”. What this really means is the TemplateInfo class metadata is populated by the parameters we use when calling a Html helper.

From a template we access the TemplateInfo class via ViewDataDictionary. In our example the most important property is the HtmlFieldPrefix, as the name suggests this what’s used to name a html field.

<%string name = ViewData.TemplateInfo.HtmlFieldPrefix;%>

If your curious you may have viewed the html source that the helper produces, for example for our DateOfBirth property:

The name used in the markup is generated by the Html helper and set to the value of the HtmlFieldPrefix. The HtmlFieldPrefix usually represents the name of the view data property that was passed by the Html helper.

Why not just hard code the name and id?
  • Wouldn’t make the template reusable and breaks the DRY principle.
  • When calling a html helper, users of the template expect the html generated for name/id to change if a HtmlFieldPrefix is passed, for example Html.TextboxFor(model=>model.DateOfBirth,”DOB”), it is expected that the name and id will be changed to “DOB”.
  • Your model won’t bind automatically, so your have to continue your hard coding into the controllers action.
  • It won’t work with nested classes or complex types and will require further redundant code in the controllers action.
What´s the replace for?
<%string id = name.Replace(".", "_");%>

This is just to deal with nested classes, in the HtmlFieldPrefix nested classes are delimited by a dot, for example to select the Foo property of a nested class, in a html helper we would use Html.TextboxFor(model=>model.NestedClass.Foo), this sets the HtmlFieldPrefix to “NestedClass.Foo”. However the textbox id is set as “NestedClass_Foo”, the replace is my crude method to mimic this standard used by other Html helpers.

If you want to dig deeper into templates and their relationship with ModelMetadata refer to Brad Wilson’s excellent post.

The jQuery UI bit?

Once we set up jQuery UI and we know that via ViewData.TemplateInfo.HtmlFieldPrefix, we can get the id of the textbox that the html helper generates, it’s very easy to get the jQuery UI date picker working.

We use the jQuery selector $(“#<%=id%>”) to select the textbox that our template will generate and then we extend it with the datepicker, the rest of the parameters are just to format the date picker and are documented on the jQuery UI website.


YES, we are mixing server and client side code here and some people hate that concept, as we are only using the server side code to output the id of the textbox, I personally have no problem with this at all.

As this recipe demonstrates, with hardly any code using the TemplateInfo class and jQuery it’s easy to create powerful reusable templates, this example including markup was less < 25 lines of code.

Data Annotations

Download the code for this example

Slightly off topic, but bare with me, data annotations (and other attributes in the System.ComponentModel namespace) can be used to annotate your model to affect the behavior of your views, templates and controllers.

Using data annotations to affect the behavior of your MVC application is known as convention over configuration. “Convention” means following a certain coding standard that the framework will recognize, the framework will provide “default behavior” matching the “convention”. Flexibility is maintained by overriding the “default behavior” the framework provides. Traditionally config files or databases are used to configure behavior, the advantage of convention over configuration is we have less/simplified code without losing flexibility, it’s self documenting and provides a coding standard.

Some people don’t like this approach, however this is my preferred approach. Naysayers will say that this approach ties your model to your domain problem and prevents you from sharing a model that has different validation requirements etc. My argument to that is “You ain’t gonna need it” (YAGNI), I’m not going to worry about a limitation (and add complexity) until it becomes a problem, then I’ll find a solution.

I’ve used this technique in 5 production MVC applications and have only ran into the model sharing problem once, if you do run into this problem an easy solution is to use “Buddy classes“, Buddy classes allow you to provide separate class metadata when required.

OK after my long rant, a couple of quick examples – one to improve your template and one to add validation.

Looking at our Index view the labels don’t look very user friendly, for example FirstName would look better as First Name. We could hard-code the labels, but that would spoil all our hard work. Instead all we are going to do is annotate our Employee model with the DisplayName attribute.

Change your employee model as follows:

public class EmployeeModel {

	[DisplayName("First Name")]
	public string FirstName { get; set; }

	[DisplayName("Last Name")]
	public string LastName { get; set; }

	[DisplayName("Date of Birth")]
	public DateTime? DateOfBirth { get; set; }
}

Debug/Employee/Index and you should see the following:

\Employee\Index using DisplayName

\Employee\Index using DisplayName

Pretty simple huh, the Html.LabelFor helper knows to pick up the label from our DisplayName attribute. Next we will use data annotations to add some basic validation.

Lets say we have a requirement to make both FirstName and LastName mandatory fields, but not DateOfBirth, forcing employees to give us their date of birth is just mean 🙂

Again we will annotate our Employee model, this time with the “Required” attribute.

Change your employee model as follows:

public class EmployeeModel {
	[Required]
	[DisplayName("First Name")]
	public string FirstName { get; set; }

	[Required]
	[DisplayName("Last Name")]
	public string LastName { get; set; }

	[DisplayName("Date of Birth")]
	public DateTime? DateOfBirth { get; set; }
}

Next we need to add a couple of lines of code to the Index and Details actions:

[HttpGet]
public ActionResult Index(EmployeeModel model) {
	return View(model);
}

[HttpPost]
public ActionResult Details(EmployeeModel model) {
	if (ModelState.IsValid) {
		return View(model);
	}
	else {
		return RedirectToAction("Index", model);
	}
}

Debug/Employee/Index, you should now not be able to submit the form without providing a FirstName and LastName.

\Employee\Index using Required

\Employee\Index using Required

How does this work?

We are using the Post/Redirect/Get (PRG) pattern, we changed the the Index action to take the EmployeeModel as a parameter, which we return as the action’s model. In the Details action we added a if statement to check that the model is valid. If the model isn’t valid or model errors are found, we redirect back to the Index action, passing the model and modelstate including any errors. Finally the view then displays the errors via the Html.ValidationMessageFor helper.

I’ve just scratched the surface on this subject, for example you can add further validation using data annotations and regular expressions etc. There is loads of information on this subject, Scott Gu’s post is a good place to start.

Complex Types

Download the code for this example

The last subject I want to touch on is complex types, I already mentioned the current limitations. This is just a short introduction to the workarounds, to get all your complex types flying with templates.

Our example so far is fine, but in the real world properties aren’t always primitives strings, ints and so on, so what about our requirement to add multiple offices to an employee? We need a property called Offices that has a type of List<Office>.

First of all lets change our model to include the new property:

public class EmployeeModel {

	[Required]
	[DisplayName("First Name")]
	public string FirstName { get; set; }

	[Required]
	[DisplayName("Last Name")]
	public string LastName { get; set; }

	[DisplayName("Date of Birth")]
	public DateTime? DateOfBirth { get; set; }

	public List Offices { get; set; }

}

Next we need to populate the office values by changing the \Employee\Index action, in a real situation you would get this from a infrastructure service such as a database.

[HttpGet]
public ActionResult Index(EmployeeModel model) {

    if (model.Offices == null) {
        model.Offices = new List(new Office[]{
                                                        new Office{ OfficeId = new Guid("ee70f79e-3840-4e8f-98eb-aa8ee12e254c"), Address = "One Microsoft Way, Redmond", Country = "United States", PostalCode = "WA 98052" },
                                                        new Office{ OfficeId = new Guid("0780b91a-7000-4860-a89c-ed6a1ea6c071"), Address = "Microsoft Campus, Thames Valley Park, Reading, Berkshire", Country = "United Kingdom", PostalCode = "RG6 1WG" },
                                                        new Office{ OfficeId = new Guid("db7a5076-c7c7-42f3-a6c6-1ead82af310a"), Address = "Centro Direzionale San Felice, Palazzo A. Via Rivoltana, 13", Country = "Italy", PostalCode = "1320090 Segrate (MI)" }
                                                    }
                                        );
    }

    return View(model);
}

Finally add the Html.EditorFor helper for the “Offices” property to our \Views\Employee\Index view:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcTempDemo1.Models.EmployeeModel>" %>


	Index




    

Index

<% using (Html.BeginForm("Details","Employee")) {%> <%= Html.ValidationSummary(true) %>
Fields <%= Html.EditorFor(model => model.FirstName) %> <%= Html.EditorFor(model => model.LastName) %> <%= Html.EditorFor(model => model.DateOfBirth) %> <%= Html.EditorFor(model => model.Offices) %>

<% } %>
<%= Html.ActionLink("Back to List", "Index") %>

Debugging \Employee\Index you will notice that the call to Html.EditorFor(model => model.Office) helper doesn’t return anything we can use, this is because the MVC framework is trying to match a default template for our Offices property.

\Employee\Index using Required

EmployeeIndex

So lets create a shared template for our Offices property:

Add Shared Editor Template

Add Shared Editor Template

Add a view by right mouse clicking on the Views\Shared\EditorTemplates folder and select “Add View”, when presented with the “Add View” dialog, do the following:

  • Name the template “Offices”
  • Check “Create a partial view”
  • Check “Create a strongly-typed view”
  • Type in “List<MvcTempDemo1.Models.Office>” as the view data class
Add List<MvcTempDemo1.Models.Office> template dialog

Add template dialog

Paste the following code into your template:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<List<MvcTempDemo1.Models.Office>>" %>

<%= Html.LabelFor(model => model) %>
<%= Html.ListBoxFor(model => model, new SelectList(Model, "OfficeId", "Country"))%> <%= Html.ValidationMessageFor(model => model) %>

As you can see there is nothing complex about the template, you just create a template that presents the type as you require. However if you debug \Employee\Index you will notice that the template is still not matched, this is because of the limitation I discussed before where the simple type name is used to select the template.

So how do we get the MVC framework to select our template?

There are a few options:

  • Pass the template name to the helper as a string:
Html.EditorFor(model => model.Offices, "Offices")
  • Use the attribute UIHint on your model property and supply the template name as a string:
[UIHint("Offices")]
public List Offices { get; set; }
  • Use the attribute DataType on your model property and supply the a name of the type as a string:
[DataType ("Offices")]
public List Offices { get; set; }

Try one of the options above and debug \Employee\Index.

\Employee\Index

\Employee\Index

As you can see the template is selected and the listbox shown. So do I have I a preferred method? Not exactly I use both the DataType attribute and passing the template name to the EditorFor helper. I don’t use UIHint, as you are supplying a template name in the model, for me that blurs the separation of concerns, yes you could argue that about the DataType attribute, but all you are doing with the DataType attribute is merely providing the name of your type as a string.

Are there any alternatives? Yes you could create your own html helper, Matt Hidinger has written an excellent post on how to do this.

Conclusion

What started out as a quick post, ended up as a mini book! I just can’t help evangelizing about MVC 2. In my own production MVC application by refactoring the views into reusable templates, I was able to reduce the view’s mark-up and code by more than 50%!

I hope you have found this post useful and now consider yourself a template ninja. In my opinion templates will allow you to clean-up your views and apply the same level of “pure” design as you would for the rest of your MVC application.

Appendix

All through this post I have said the easiest way to add a view is by right mouse clicking on the view’s containing folder, there is another way by right mouse clicking when you are in the code of a controllers action. I prefer to use the former method when writing documentation, to help highlight the folder structure’s relationship with controllers.

Add view from action code

Add view from action code

41 Responses to “ASP.NET MVC 2 Templates”

  1. Daniel15 says:

    Nice post 🙂

    To make the labels bold, ideally you should be using CSS instead of adding additional HTML markup. And the JavaScript (for the calendar) should be unobtrusive; the HTML source should not have any JavaScript in it other than the script references. Date fields should have a class=”date” or similar, and then some JS that runs on page load should add the calendar button.

    Although I guess this post is really about ASP.NET MVC, and in that regard, it’s a great post. Nice work 🙂

    • DalSoft says:

      Thanks I completely agree with your comment. I should of used separate JavaScript/CSS files and used jQuery to select the elements by class name for example $(“.dateField”).datepicker();

      The reason for not doing so is I tried to keep the steps as simple as possible, in an effort to highlight the MVC 2 template features as much as possible. Next time I’ll add a note of what should be done in a production scenario.

      The only thing I’ll add to your comment is you shouldn’t rely on JavaScript alone, you should also have equivalent functionality in your server side code, for example if you are validating a date in JavaScript, you should have equivalent server side code for security and usability reasons. In MVC this can be achieved with very little code using data annotations.

  2. Steve says:

    Great post!

    I can’t see the code in the code snippets though – just the html parts ?

    Thanks again!

  3. oVan says:

    Could it be that you pasted the wrong sample code below this text “To use the built-in templates just replace the Html.TextBoxFor helper with the Html.EditorFor helper, the mark-up will look like this:”? It is identical to the snippet above it.

  4. oVan says:

    My previous remark is only valid on Google Chrome: about all code snippets are displayed incorrectly. I just tried it in Firefox and it looks correct. Please put a note at the start of the article (or maybe you can fix the Chrome problem?) Thank you for your interesting post!
    Olivier.

  5. Daniel Eagles says:

    Great post thank you, everything I need to know about MVC 2 in one post. For some reason though the ASP code snippets only work in firefox.

  6. DalSoft says:

    OK I’ve got the ASP.NET snippets working in Chrome, Firefox and Safari. It’s still not perfect as it’s not obeying the tabs. I’ll test IE7-8 tomorrow.

    I’m going to look for a better syntax highlighter plug-in, any suggestions for one that supports ASP.NET properly are welcome.

  7. coderY says:

    Thank you so much for sharing this with us.

  8. […] ASP.NET MVC 2 Html.Editor/Display Templates: A great blog post detailing the new Html.EditorFor() and Html.DisplayFor() helpers within ASP.NET MVC 2. […]

  9. Saeid says:

    Great post, Finally MVC2 Templates became clear to me, thanks for that

  10. Great article.
    I have migrated a couple of apps to MVC2 and being able to rip out ugly views and replace them with a consistent UI using templating is a real pleasure.
    You give some great tips on using templates.

  11. Wojtek says:

    Hi, template explanation is great, yet I’m not able to run your example on complex types. I mange to use template for Offices and display is correct. Unfortunately it doesn’t bind selected offices to the model after post. I suppose that to bind to list of complex types I need to post form values like: Offices[0].OfficeId = “guid_1”, Offices[1].OfficeId = “guid_2”, …
    But dropdownlist doesn’t allow to post such values, it can post only multiple parameters having the same name (no indexing).
    Could you help, to make your example working? Regards.

  12. […] It's difficult to get across how awesome this is without writing a whole post about it, fortunately someone's already done that for me and I can't recommend it […]

  13. Gavin says:

    This post is what I’ve been looking for a long time. I would love to see an example of how you would create a template for selecting the Employee’s Department.

  14. […] MVC 2 Templates May 21 […]

  15. Jason says:

    Awesome post! Exactly what I’ve been looking for! Excellent job! Keep it up!!:)
    Your biggest fan here! LOL

  16. J Dubs says:

    Many thanks for the detailled blog post. Having downloadable examples has really helped.

    I should point out that I did have a problem with the UrlParameter.Optional reference in the global.asax file. As I’ve been playing around with this for a while I decided to uninstall all the MVC installs 2.0 & 1.0 and then reinstall the latest version. This solved my only problem.

    Cheers !

  17. Ian Turner says:

    I too love templates – for instance I’m writing an application form view that calls a “person” template (several times) that calls a “postal address” template several times…

    However, and this is a big however, the HTML renders controls with, for each address, the same “id” attribute, say “line1” for the first address line, when in fact my model binding requires ids like e.g. “person1.homeaddress.line1”, “person2.officeaddress.postcode”…

    Any suggestions very gratefully received, because at the moment I’m considering a custom version of Html.Partial that accepts a “model prefix” parameter, and pfexing the ids with that.

    TIA
    Ian

  18. Riddhi says:

    Hi..
    Many Thanks for your post…Its realy help me a lot…

    Thank you

  19. […] April I did a comprehensive blog post about the Html.EditorFor() and Html.DisplayFor() helpers in MVC 2, and there use with templates. It […]

  20. Carlos H says:

    Hi, is there a way to change the icon which appears next to the datepicker calendar?
    I kinda don´t like it, I´d rather put a small calendar image there for my application but I don´t know how.

    Thanks.

    • DalSoft says:

      Yes change the DateTime template, add the buttonImage parameter to the datepicker function example below:

      $(document).ready(function() {

      $(“<%=id%>“).datepicker({
      showOn: ‘both’,
      buttonImage: “images/calendar.gif”,
      dateFormat: ‘dd M yy’,
      changeMonth: true,
      changeYear: true
      });
      });

  21. Kevin says:

    What if you create an html helper extension for the EditorFor(model => model.Property, “MyCustomEditor”)

    For example, you have to render a date picker, instead of call EditorFor(…) for the date picker, you call Html.DatePickerFor(…)

    … DatePickerFor(…)
    {
    return EditorFor(…, “DatePicker”)
    }

    This will keep the magic string to a minimum and is much nicer for other developers to use. What are your thoughts on this?

    • DalSoft says:

      You don’t need to use magic strings with templates when dealing with non complex types, just simply name the template DateTime.ascx. For complex types you can use the DataType attribute as described in the post.

      For example Html.EditorFor(model => model.MyDateTimeProperty) works when you name your template DateTime.ascx convention over configuration takes over and the template is selected using the property’s type name.

      Writing your own html helper extension is a solution, but you are rendering html/javascript using code which could be problematic, for example you will need to write a helper to replace each template, rather than using simple mark-up in a template.

      I personally like the flexibility of being able to change templates easily by dropping files into the DisplayTemplates or EditorTemplates folder whenever I want to change how my Model’s properties are displayed. In the end though I guess it’s a matter of taste and what works for your situation.

  22. Daniel Liuzzi says:

    DalSoft,

    Great article with lots of useful information. A little nitpick: the preferred nugget to use in MVC 2.0 uses the colon instead of equal sign. (i.e. instead of (my apologies if WordPress strips the angle brackets). Equal sign nuggets are no longer recommended, because they are vulnerable to XSS attacks if you forget to encode your output. By using the colon instead of the equal sign, encoding is done automatically for you, so no more ugly Html.Encode()’s on your views.

    Cheers.

  23. Roberto says:

    Great article, DalSoft! Thank you.

    One problem though: when I supply an instance of EmployeeModel to the View, an exception is raised:

    The model item passed into the dictionary is of type ‘System.Int32’, but this dictionary requires a model item of type ‘System.String’.

    I would expect the Int32 property to default to the default editor (i.e. the build in editor).

    [HttpGet]
    public ActionResult Index()
    {
    return View(
    new EmployeeModel
    {
    Id = 12
    });
    }

    Btw, is it possible to override the selection of templates. It would be nice if various datetime pickers could be used (depending on the user’s preferences).

    Ciao

  24. Marshall says:

    What if you wanted to add to the list of Offices instead of selecting an existing one on the same view? How do you display a text boxs for Office Address and Country?

    Thanks,
    -Marshall

  25. […] Blog jQuery plugin: Tablesorter 2.0 DataTables (table plug-in for jQuery) jQuery Grid Plugin ASP.NET MVC 2 Templates Take a Mulligan with Redo Backup & Recovery | Linux Magazine http://www.Visual6502.org MetaSharp […]

  26. Gene Osteen says:

    I’m stuck at trying to add the calendar. I keep getting the below error every time I add the DateTime View to the Editor Templates. when I remove the view everything works fine.

    {“The model item passed into the dictionary is null, but this dictionary requires a non-null model item of type ‘System.DateTime’.”}

  27. swapna says:

    wow!! such a great article!!!

  28. narasimha says:

    Excellent article. You can publish a book with such excellent content

  29. Cody Sternberg says:

    You have a great blog right here! With content like this you should write a MVC book.

  30. henkel-partner.net says:

    Hey there, I think your website might be having browser compatibility issues.
    When I look at your website in Firefox, it looks fine
    but when opening in Internet Explorer, it has some overlapping.
    I just wanted to give you a quick heads up!
    Other then that, excellent blog!

  31. superiorbhb says:

    Great Information.Thanks for sharing 🙂

Leave a Reply to swapna

preload preload preload