Day 8: Semi-Automatic Bootstrap – Editor Templates

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

Now that we have a nice way to consistently display our data, what about editing it? A label is fine to indicate what the saved value is, but it doesn’t really solve the issue of input.

Introducing Editor Templates

Much in the same way that a shared view can act as the de facto template for rendering data (as we saw yesterday), you can override the default editor output by the framework. Create a new view in Views\Shared\EditorTemplates called Boolean.cshtml and put the following code in it:

@model bool?

@{
// make use of nullable class attribute values
var yesSelected = Model.HasValue && Model.Value ? “active” : null;
var noSelected = (Model.HasValue && !Model.Value)
|| (!Model.HasValue && ViewData.ModelMetadata.IsNullableValueType)
? “active”
: null;
var noSelection = !Model.HasValue ? “active” : null;

<span class="rem">// get the name of the ID - this is to support multiple fields</span>
var htmlField = ViewData.TemplateInfo.HtmlFieldPrefix;

}

@Html.HiddenFor(model => model)

<div class=“btn-group” data-toggle=“buttons”>
<label class=“btn btn-info @yesSelected”>
<input type=“radio” class=“bool-@htmlField” onchange=“javascript:$(‘#@htmlField’).val(true);” /> Yes
</label>
<label class=“btn btn-info @noSelected”>
<input type=“radio” class=“bool-@htmlField” onchange=“javascript:$(‘#@htmlField’).val(false);” /> No
</label>

@<span class="kwrd">if</span> (ViewData.ModelMetadata.IsNullableValueType)
{
    &lt;label <span class="kwrd">class</span>=<span class="str">"btn btn-info @noSelection"</span>&gt;
        &lt;input type=<span class="str">"radio"</span> <span class="kwrd">class</span>=<span class="str">"bool-@htmlField"</span> onclick=<span class="str">"javascript:$('#@htmlField').val('');"</span> /&gt;Do Not Set
    &lt;/label&gt;

}

</div>

There are two important pieces to note in the above code, namely the inspection of TemplateInfo and ModelMetadata in the ViewData instance presented to our view, and the hidden backing field that is kept in sync via JavaScript. ViewData is a ViewDataDictionary that contains, as those properties suggest, metadata about the type of model being used, information about the template, and other view-specific data.

To see this new template in action we’ll have to get a Create view set up and an action on our controller. Head back to the SimpleController class and add the following code:

public ActionResult Create()
{
var person = new Person();
return View(person);
}

Now, right-click on the name of the method, and select “Add View…”, then set it up to use the Create template for the Person class.

image

Visual Studio will throw you into the editor for your new view, and you can press CTRL+F5 to see your new default control for Boolean values, or navigate to http://localhost:_port_/Simple/Create to see the page.

The scaffolded view contains simple calls to HTML helpers and doesn’t know anything about the instance of the Person that will be created or the fact that you’ve created a new template to render Boolean values. You’ll only see the following:

@Html.EditorFor(model => model.LikesMusic)

As well, the templates are rendered on the fly by the view engine (just like all views) so you don’t need to recompile as you make updates. Feel free to experiment with the template code and just refresh in your browser after you save.

Note that our Person class doesn’t have a nullable Boolean value, but it if did, it would render like so because of our evaluation of the ModelMetadata in the template above:

image

Controlling the Use of Custom Templates

Now these new controls – button groups for input and labels for display – work great, but you may not wish to use them for all Boolean properties. In both the DisplayTemplates and EditorTemplates folders, rename the Boolean template to BooleanButtonLabel.cshtml. Then, return to your Person class and decorate the LikesMusic property as follows:

[UIHint(“BooleanButtonLabel”)]
public bool LikesMusic { get; set; }

There may be scenarios where you wish to use several alternate templates to display or edit data. This attribute gives the MVC Framework directions to use a template of our choosing, rather than having to use the same template for all properties.

Next Steps

You can now render simple properties with whichever template you wish, but what about more complex types? Tomorrow we’ll look at using a custom template at a class level.