UIHint, DisplayTemplates, and EditorTemplates in MVC

I’ve been doing a good amount of programming lately, but I haven’t been good about writing down what I’ve been learning! Worse, is I should be writing down things that I keep forgetting – like this topic, today: how to use UIHints in Entity Framework objects to help display data appropriately!

Let me start from the beginning – there is a truly magical formula, in my mind, of using:

Entity Frameworks + the Repository Pattern + MVC + jQuery = Magic!

In my career, I’ve yet to see anything as elegant as this suite of technologies. Several aspects of these work very powerfully together! I am working on building a site that uses just these technologies, and it will be HTML5 too, in the end.

One particularly cool feature is the idea that MVC can show data and do validation based on how the “model” is decorated with attributes. In particular, there is an attribute you can use, that MVC understands, that gives MVC a hint on how to display the data. I wanted to explain how that works for Future Robert, and anyone else that might be interested!

STEP 1: The Model
So first, this is assuming that we are using an Entity Frameworks model. You can use a model-first or code-first (POCO) type of model, it doesn’t matter. Although, if you are using model-first, you really have to do some fancy things to work around the auto-generated code. I would highly recommend converting those model-first generated classes to POCO’s, that would give you a lot more freedom to take advantage of things like this! OK, so on an entity class I have some definitions of properties that are defined like this:

/// <summary>
/// Gets or sets the e-mail address of the current author.
/// </summary>
[Required]
[Display(Name = "E-mail Address:", ShortName = "E-mail:", Description = "The e-mail address of the author.")]
[RegularExpression(ValidationHelper.RegularExpressionForEmail)]
[StringLength(100)]
[UIHint("Email")]
public virtual String EmailAddress { get; set; }

/// <summary>
/// Gets or sets the title of the current author.
/// </summary>
[Required]
[Display(Name = "Title:", ShortName = "Title:", Description = "The title of the author.")]
[RegularExpression("^([a-zA-Z '-]+)$")]
[StringLength(100)]
[UIHint("MediumTextBox")]
public virtual String Title { get; set; }

/// <summary>
/// Gets or sets the biography of the current author.
/// </summary>
[Required]
[Display(Name = "Biography:", ShortName = "Biography:", Description = "The biography of the author.")]
[RegularExpression("^([a-zA-Z '-]+)$")]
[StringLength(100)]
[UIHint("LargeTextArea")]
[AllowHtml]
public virtual String Biography { get; set; }

Note in particular the [UIHint] attribute. That is nothing more but a way for me, in the model, to give a hint to the UI (whatever that may be) of how best to display this field of data. By the way, I’m a big fan of also using attributes to specify the length, a regular expression, and even what the label for this data should be. Again, you can’t easily do that with generated-classes with EF – so c’mon, convert your classes to POCO’s!

STEP 2: The MVC View
Now, the MVC view doesn’t do much of anything interesting, it just looks the way you’d expect:

<tr>
<td class="display-label">@Html.LabelFor(model=>model.EmailAddress)</td>
<td class="display-field">
@Html.DisplayFor(model => model.EmailAddress)
</td>
</tr>
<tr>
<td class="display-label">@Html.LabelFor(model=>model.Title)</td>
<td class="display-field">
@Html.DisplayFor(model => model.Title)
</td>
</tr>
<tr>
<td class="display-label">@Html.LabelFor(model=>model.Biography)</td>
<td class="display-field">
@Html.DisplayFor(model => model.Biography)
</td>
</tr>

This is part of why this is so cool. You don’t have to do anything special inside the view. The MVC framework inspects the underlying property and based off the annotations, it will add the label we specified, and add field validators for us automatically.

But what about that UIHint? Well, in any view, including Shared, you can create a DisplayTemplates and EditorTemplates folder. In those folders, if you create a partial view that has the name of that UIHint, MVC will pass that property to the partial control and leave it to figure out how to display it.

Now wait a second, that is almost amazing. What I mean is, in my data model, I set a UIHint of “Email”. So now, in my DisplayTemplates, I can create a partial view called Email.cshtml (I’m using MVC3 with Razor) – and in that file, I can display that field as a mailto link, for example!

STEP3: The Templates
Ok, under ViewsShared, create 2 new directories: DisplayTemplates and EditorTemplates – those should both be children under the Shared folder. Within the DisplayTemplates folder, create a new partial view called e-mail:

image

Inside of that file, put something like this (and only this):

<a href="mailto:@Model">@Model</a>

Now, anywhere on your site, if you use @Html.DisplayFor for that field, it will automatically show as a mailto link – EVERYWHERE!

I have another DisplayTemplate for Hyperlink (in Hyperlink.cshtml – which corresponds to UIHint(“Hyperlink”)) – that looks like this:

<a href="@Model" target="_blank">@Model</a>

Now, editor templates can be a little more fickle. However, this is how I approached it – for 3 common needs: 1) a medium textbox, 2) a medium text area and 3) a date/time field. For each of these, I pushed out whatever I could to a corresponding CSS class. That way, things like height, width, font, etc would be consistent and easy to change in one place. So, here are those 3 common ones I use a lot:

MediumTextArea.cshtml (corresponds with UIHint(“MediumTextArea”)):

@Html.TextAreaFor(item=>item, new { Class="medium-text-area"})

MediumTextBox.cshtml (corresponds with UIHint(“MediumTextBox”)):

@Html.TextBoxFor(item=>item, new { Class="medium-text-box"})

DateTime.cshtml (corresponds with UIHint(“DateTime”)):

@{
String randomCssClassName = "DateTimeTextBox_" + new Random().Next(10000, 99999).ToString();
}
@Html.TextBoxFor(item => item, new { Class = "date-time " + @randomCssClassName })
"text/javascript" language="javascript">
$(function () {
$(".@randomCssClassName").datepicker({
changeMonth: true,
changeYear: true,
defaultDate: new Date('@Model')
});
});

This DateTime one is a little goofy, so let me explain what is going on. I wanted to show a jQuery UI datetime picker whenever I am editing a date field, on any table. However, jQuery needs to be able to find this textbox. I can’t give is a hard-coded ID because there might be more than one datepicker on the page.

Also, I can’t use the “id”, because MVC uses that while it’s keeping track of the model, during editing. jQuery can select things by CSS class, so I created a bogus CSS class, solely for the purpose of jQuery to be able to select the textbox.

So, the Razor code generates a random CSS class that will look something like “DateTimeTextBox_20394”. Then, I do a .TextBoxFor to render it to the screen, specifying the css class. You might notice that I use upper-case “Class” instead of “class”, that is because “class” is a reserved word.

After that, is just the jQuery UI code to show the DateTime picker. This assumes that the masterpage (_Layout.cshtml) is loading jQuery and jQuery UI already.

So, that’s all there is to it. You decorate your Model properties with UIHint. Then, you create a matching EditorTemplate or DisplayTemplate with a matching name. So long as you use DisplayFor or EditorFor (or EditorForModel, for that matter) – this all just magically works!

Oh, and Future Robert? You’re welcome.

Tagged with: , ,
Posted in .NET 3.5, .NET 4.0, ASP.NET, ASP.NET MVC, Best-practices, Entity Framework, JQuery, SQL, Uncategorized
3 comments on “UIHint, DisplayTemplates, and EditorTemplates in MVC
  1. Steferson says:

    You can get the id for the field by using

    @{
    string name = ViewData.TemplateInfo.HtmlFieldPrefix;
    string id = name.Replace( “.”, “_” );
    }
    in your partial view, so you don’t have to generate a random css class to access your DateTime field

    Like

  2. Mjcarrabine says:

    Is there a way in Razor to use the ShortName annotation from the model instead of DisplayName?

    Like

  3. Rob Seder says:

    @Steferson I did that CSS trick because this might be a partial view – and OTHER partial views might also use the same controls, and there would be a name conflict!

    @Mjcarrabine I don’t know, sorry!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
Categories

Enter your email address to follow this blog and receive notifications of new posts by email.

Join 5 other followers

%d bloggers like this: