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.



Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date