Download a complete solution for this article here

Using ASP.NET MVC 3 and jquery, we can easily and automatically validate and format data such as phone numbers and social security numbers into user friendly formats like (xxx) xxx-xxxx or xxx-xx-xxxx, not only when data is presented to the user, but also while the user is typing in data! We can also block the user from even entering non-numeric characters if we wish. We can parse that data after a form submit and strip out the parentheses and dashes before it reaches the controller action. We can also easily control the appearance of the input textboxes of all the fields in all the forms in one easily modified location. What’s nice about the approach presented here is that the developer only needs to designate a model field as a social security or phone number in one location (the view model) and the system handles all the html, jquery, and data cleanup automagically. This is done without using the class html attribute on any of the input controls, which frees the developer to use css classes as normal. Furthermore, this approach is compatible with Microsoft’s jquery unobtrusive client-side validation, so you can present the user with friendly highlighting and popups before invalid data is even sent to the server.  Put together, these techniques create a crisp, fluid experience for the users and a fast, easy development environment for the developers.

Jquery.maskedinput is a great tool for formatting user inputs, however the input masking not only changes the data the user sees but also changes the data posted to the controller action from the automatic model binding during the form submit. If the database field requires all numeric characters or dashes instead of parenthesis, you’ve got a mess to clean up. Furthermore, manually attaching masks to every field in every view is pretty tedious. This is where ASP.NET MVC really comes to the rescue with custom model binders and Editor Templates to automatically handle all that formatting, masking and unmasking for you. While there are certainly a lot of ways you can skin the cat with jquery and ASP.NET MVC, this article has some examples that work extremely well together using the latest technologies.

The examples here use jquery 1.4.4, jquery.UI 1.8.9, jquery.maskedinput 1.2.2 and ASP.NET MVC 3. You can find jquery and jquery.UI on the jquery.com site, and jquery.maskedinput.js on the plugins.jquery.com website. Be sure to add a content reference in your master page to your js files in your project.

Attribute tags for our model fields

We need a way of telling the custom model binder (which we will create later) that a model field is a social security number or phone number, and the way we’re going to do this is with a couple of stubbed out custom Attributes. These attribute tags are just empty raw Attributes now, but can be embellished with validation or other tricks later.

public class PhoneNumberAttribute : Attribute { }
public class SocialSecurityNumberAttribute : Attribute { }

Our model fields can now be tagged like this:

public class MyModel
{
    [SocialSecurityNumber]
    public string SSN { get; set; } 

    [PhoneNumber]
    public string HomePhone { get; set; }
}

Also, since this approach is compatible with MVC3′s unobtrusive client side validation, we can also tag the fields with [Required] or other validating data annotation tags.

ViewData extensions

We’ll need to easily fetch the attribute tags from our model fields in our Editor Template, so we’ll need this ViewData extension method created in a static class somewhere in your project:

public static TAttribute GetModelAttribute<TAttribute>
     (this ViewDataDictionary viewData, bool inherit = false)
      where TAttribute : Attribute
{
    if (viewData == null) throw new ArgumentNullException("viewData");
    var containerType = viewData.ModelMetadata.ContainerType;
    return (
        (TAttribute[])containerType.GetProperty(viewData.ModelMetadata.PropertyName)
            .GetCustomAttributes(typeof(TAttribute), inherit))
            .FirstOrDefault();
}

We’ll want to easily add style tags and other html attributes to form fields. Html attributes are passed to the Editor Template as key values in the Editor Template’s ViewData. We can create this ViewData extension method that lets us easily add style segments to the Editor Templates ViewData html attributes by appending a style segments to the “style” string in ViewData.

public static void AddStyle(this ViewDataDictionary viewData, string newStyleSegment)
{
    if (!newStyleSegment.EndsWith(";"))
    {
        newStyleSegment += ";";
    }

    // find existing style object and append with new style segment
    if (viewData["style"] == null)
    {
        viewData.Add("style", newStyleSegment);
    }
    else
    {
        //  cast as nullable string in case some other ViewData
        //  object named style happens to be in there
        string style = viewData["style"] as string;
        if (style != null)
        {
            if (style != "" && !style.EndsWith(";"))
            {
                style += ";";
            }
            style += newStyleSegment;
            viewData["style"] = style;
        }
    }
}

Editor Templates

Editor Templates conveniently give you a great deal of power by allowing you to automatically format all datatypes the way you like without having to clutter up your view pages. In our example we have phone numbers and social security numbers that are both strings in our model, but need to be formatted differently. A simple way of doing this is to override the String.ascx Editor Template and use a switch to change the formatting for the social security and phone numbers. We use this Editor Template to add a jquery.maskedinput on each of the input boxes that block the users from adding anything other than numeric characters and puts their inputs into our preferred formats as they type.

In your Views folder, under the Shared folder, create an EditorTemplates folder if you don’t already have one, and add a “MVC View User Control (ASPX)” control named String.ascx (yes, Visual Studio 2010 says it’s an ASPX when it’s actually an ascx).

Notice in the example below that the view control’s ViewData is passed directly to the htmlAttributes object in the Html.TextBox. Our AddStyle ViewData extension uses that feature to format the input boxes with our own style tags such as width. Your String.ascx will look something like this:

<%@ Control Language="C#"
     Inherits="System.Web.Mvc.ViewUserControl<System.String>" %>
<%@ Import Namespace="System.Web.Mvc.Html" %>
<%@ Import Namespace="MyProject" %>

<%-- Phone Number --%>
<% if (ViewData.GetModelAttribute<MyProject.PhoneNumberAttribute>() != null) { %>
    <%: Html.TextBox("", Model, ViewData) %>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#<%: ViewData.ModelMetadata.PropertyName %>").mask('(999) 999-9999');
        });
    </script>
<%-- Social Security Number --%>
<% } else if
     (ViewData.GetModelAttribute<MyProject.SocialSecurityNumberAttribute>() != null) {
        ViewData.AddStyle("width:75px");
%>
    <%: Html.TextBox("", Model, ViewData) %>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#<%: ViewData.ModelMetadata.PropertyName %>").mask('999-99-9999');
        });
    </script>
<%-- default string --%>
<% } else { %>
    <%: Html.TextBox("", Model, ViewData) %>
<% } %>

In MVC’s convention over configuration scheme, the ASP.NET MVC engine will automatically use the String.ascx to produce any model field of the string datatype when a view calls EditorFor. In your view, you just type <%: Html.EditorFor(x => x.SSN) %> and MVC will handle the rest.

ALTERNATIVE: Instead of lumping all the model types into the String.ascx, you could create a separate Editor Template for SocialSecurityNumber.ascx and PhoneNumber.ascx, but then you’d have to call EditorFor(x=>x.SSN, “SocialSecurityNumber”) instead of just EditorFor(x=>x.SSN). I prefer to lump them all in the String.ascx because the developers only have to set the type in one place (the model field) instead of in both the views and the view models. There’s probably a little bit of a performance hit with the lump String.ascx architecture because of the switch in the String.ascx, so race car sites may want to go with the split Editor Template alternative.

Custom Model Binder

Finally, we need to take the user’s input coming from the form fields on its way to the controller action during a form submit and strip the masking characters, returning it to a state the rest of our code is expecting. Specifically for our example, we want to take social security numbers and phone numbers and strip out the parentheses and dashes we added on the form. This is easy to do with a custom model binder that overrides ASP.NET MVC’s default model binder which controls how form fields are automagically mapped to the view model posted to a controller action during a form submit here:

[HttpPost]public ActionResult MyAction(MyModel viewModel)

In our custom model binder we want to override the DefaultModelBinder’s SetProperty method and find all strings that are tagged with our social security or phone number attribute tags. Then we use regex to strip out anything that is not a numeric field. We’ll also take this opportunity to clean up the user inputs by removing any whitespace padding. It doesn’t get much simpler than this:

public class CustomModelBinder : DefaultModelBinder
{
    protected override void SetProperty
     (ControllerContext controllerContext, ModelBindingContext bindingContext,
      PropertyDescriptor propertyDescriptor, object value)
    {
        if (value != null && propertyDescriptor.PropertyType == typeof(string))
        {
            //  trim all strings to clean out any whitespace padding
            value = ((string)value).Trim();

            if ((string)value == string.Empty)
            {
                value = null;
            }
            else if ((propertyDescriptor.Attributes[typeof(PhoneNumberAttribute)] != null

                || propertyDescriptor.Attributes[typeof(SocialSecurityNumberAttribute)])
                && bindingContext.ValueProvider.GetValue(propertyDescriptor.Name) != null
                && bindingContext.ValueProvider.GetValue(propertyDescriptor.Name).AttemptedValue != null)
            {
                value =
                    Regex.Replace
                     (bindingContext.ValueProvider.GetValue(propertyDescriptor.Name).AttemptedValue,
                                  "[^0-9]", "");
            }
        }

        base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
    }
}

Now we just need to register our custom model binder in the global.asax.cs’s Application_Start method:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
    ModelBinders.Binders.DefaultBinder = new CustomModelBinder();
    …
}

From now on, developers just need to decorate their model fields with [SocialSecurityNumber] or [PhoneNumber] and use Html.EditorFor in their view’s like this:

 <%: Html.EditorFor(x => x.SSN) %>

That’s it, enjoy!

« »