Writing Custom MVC3 Form Widgets

ASP.NET MVC3
with Razor
offers incredible unobtrusive client side validation of your server side
defined models. This pattern allows you to define the validation rules on your
model and have them validated on the client using slick, professional and
efficient jQuery validation
library right out of the box. To learn how to use unobtrusive jQuery
validation, check out ‘Unobtrusive
jQuery Validation Using MVC3 and Razor
‘. While unobtrusive MVC3 model validation is an
incredible time saver, in real world web projects you will need to develop
custom widgets or adapt 3rd party widgets to work within the
unobtrusive validation framework. Fortunately MVC3 makes this quick and easy to
do.

Editor Template

In either your View’s folder or the Shared folder, create a
directory called EditorTemplate. This is a special directory name that ASP.NET
MVC3 looks in to find defined editor templates. After you’ve created your
EditorTemplate directory, right click the folder and choose “Add New Item”. In
the new file dialog, choose “MVC3 Razor ViewPage”. Create the page with no
layout file defined.

At the top of your page, add an inherits command inheriting
your page from WebViewPage with the type set as the data type of your model.
The example below shows a WebViewPage with an Int32 model. The rest of your
ViewPage will be the HTML that is rendered out to the page.

@inherits System.Web.Mvc.WebViewPage<System.Int32>

In order to give the unobtrusive validation framework
something it understands to validate, render a hidden field to the page binding
to the model. Whatever your custom UI widget does, have it update the value in
this hidden field to indicate the users selections made in your custom UI.

@Html.HiddenFor(model => model)

You need to be able to read the validation parameters set
for the element to know what rules to enforce and how to render that up to the
UI. The information about the validators being applied to your element can be
accessed through the @ViewData.ModelMetadata.GetValidators(this.ViewContent)
method. You can then search through this collection to find the attributes you
can support, and then pull those values out to use in your component.

The code example below looks through the validation rules on
the current component and gets the min and max value defined on the range
attribute.

foreach (var r in
@ViewData.ModelMetadata.GetValidators(this.ViewContext))
{
if (r.GetType() == typeof(System.Web.Mvc.RangeAttributeAdapter))
{
foreach (ModelClientValidationRule mcvr
in ((System.Web.Mvc.RangeAttributeAdapter)r).
GetClientValidationRules())
{
MinValue=(int)mcvr.ValidationParameters["min"];
MaxValue=(int)mcvr.ValidationParameters["max"];
}
}
}

Below is the complete listing for the custom editor template
that adapts jQueryUI’s slider element to the MVC3/Razor framework.

@inherits System.Web.Mvc.WebViewPage<System.Int32>
@Html.HiddenFor(model => model)
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td style="width:75px"><div id="CaptainSaltinessPercentText">@Model.ToString() %</div></td>
<td style="width:400px"><div id="CaptainSaltinessPercent_Disp"></div></td>
</tr>
</tbody>
</table>

<script language="javascript">
    $(function () {
        @{
            int MinValue = 0;
            int MaxValue = 200;
foreach (var r in
@ViewData.ModelMetadata.GetValidators(this.ViewContext))
{
if (r.GetType() == typeof(System.Web.Mvc.RangeAttributeAdapter))
{
foreach (ModelClientValidationRule mcvr
in ((System.Web.Mvc.RangeAttributeAdapter)r).
GetClientValidationRules())
{
MinValue=(int)mcvr.ValidationParameters["min"];
MaxValue=(int)mcvr.ValidationParameters["max"];
}
}
}
        var MinValue = @MinValue;
        var MaxValue = @MaxValue;

        var myRef = "@ViewData.ModelMetadata.PropertyName";
        $("#"+ myRef +"_Disp").slider({
            value: @Model.ToString(),
            min: MinValue,
            max: MaxValue,
            step: 5,
            slide: function (event, ui) {
                $("#"+ myRef).val(ui.value);
                $("#"+ myRef +"Text").html(ui.value +"%");
            }
        });
    });
</script>

Applying custom views to your model

Once you’ve defined your custom view you can apply it to
your model by using the UIHint attribute. This attribute’s name should match up
with the file name you put in your EditorTemplate directory. Similar to other
named files and views in MVC3, the framework will search for the first file
that matches that name in the EditorTemplates directory of the current view and
if it doesn’t find one there, it will check the Shared folder.

The code snippet below defines a simple model with a single
property called CaptainSaltinessPercent. The property uses the Required and
Range validation attributes defined in theB
System.ComponentModel.DataAnnotations namespace. To use the DataAnnotations,
you’ll need to reference the System.ComponentModel dll as well.

public class Boat
{
[UIHint("Slider")]
[Required,Range(0,100)]
public int CaptainSaltinessPercent { get; set; }
}

The View Page

In an ASP.NET MVC3/Razor validated page, you use the Html helper
methods to render the label, the widget and the container that will hold the
validation message so that the configuration of your model can flow seamlessly
into your view without your view having to know the details of what your model
needs.

To call custom editors, use the Html.EditorFor helper. This
helper searches the model for the appropriate element defined by the model. If
you want to change the widget that renders your model later, you can adjust the
UIHint in the model and the view will automatically render the changed
component.

<div class="editor-label">
@Html.LabelFor(model => model.CaptainSaltinessPercent)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.CaptainSaltinessPercent)
@Html.ValidationMessageFor(model => model.CaptainSaltinessPercent)
</div>

Conclusion

No validation framework is complete without supporting
powerful extensibility so you can meet your project’s special needs. ASP.NET
MVC3 and Razor’s powerful unobtrusive validation capabilities can be easily
customized to support custom UI widgets.

More by Author

Must Read