MVC validation part 1: Custom unobtrusive validation using attributes

The validation process you get from MVC is pretty damn sweet. Just annotate your class, or entity for that matter, and you get instant validation in both your views and in your repository. It’s easy enough to use, just annotate your properties with whatever validation you want to use, and the validation appears. For reference, this is how it’s done:

 

public class MyValidateableObject 
{
    [Required(ErrorMessage = "You have to input something")]
    [StringLength(50, ErrorMessage = "It's too long!")]
    public string MyString { get; set; }
}

To get this to work with MVC, you just use Html.EditorFor(…) as you normally would. Also include a Html. ValidationMessageFor(model => model.MyString) to get the validation message to the client. To enable the client side unobtrusive validation in your app, add this to your Web.Config

<appSettings>
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

Also, do not forget to include the javascript files. You can typically do this in _Layout.cshtml, since your application probably will use these in most if not all of the views.

    <script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>

With that out of the way, how do you validate something that isn’t already provided by the validators? And how to make this work client side? Here’s how!

First, you need to create a new attribute to use. Here we are going to create a validation that will accept any string with a length evenly dividable by a specific number. The annotation is pretty straightforward:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
    public class StringLengthDivideableAttribute : ValidationAttribute
    {
        public int Divisor {get;set;}

        protected override ValidationResult IsValid(object v, ValidationContext validationContext)
        {
            if (v != null)
            {
                string value = (string)v;

                if (value.Length % Divisor != 0)
                {
                    return new ValidationResult(ErrorMessage, new [] { validationContext.MemberName });
                }
            }

            return ValidationResult.Success;
        }
    }

Note that the class name must end with Attribute. There are two methods to override, one which does not have a validationcontext available, and one who does. I suggest overriding the one with the validation context. It’s far more flexible than the other one. With this annotation in place you should be able to add this to your class

public class MyValidateableObject 
{
    [Required(ErrorMessage = "You have to input something")]
    [StringLength(50, ErrorMessage = "It's too long!")]
    [StringLengthDivideable(Divisor=2, ErrorMessage="Length not divisible by two")]
    public string MyString { get; set; }
}

Doing this will cause server side validation, and if you use it, entity framework validation validates too. But you won’t get the really nice unobtrusive validation. To get that, we need to provide three things: Flag the attribute as client validateable, provide a mapping for unobtrusive to use and make our own javascript function. Lets start with the attribute.

Change the attribute to also implement the IClientValidatable interface. This is found in System.Web.Mvc. This interface requires you to implement one method. Here’s what you need to do:

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
        {
             var rule = new ModelClientValidationRule
                             {
                                 ErrorMessage = ErrorMessage,
                                 ValidationType = "stringdivisible"    
                             };
             rule.ValidationParameters.Add("divisor", Divisor);
             yield return rule;
         }

The ValidationType string is the important bit. That’s the key for the unobtrusive binding, which is the next step. Equally important is the ValidationParameters, which will become accessible to your javascript validator. Speaking of that, after you’ve included your jquery.validate.unobtrusive.js, add this:

jQuery.validator.unobtrusive.adapters.add('stringdivisible', ['divisor'], function (options) {
    var params = {
        divisor: options.params.divisor
    };

    options.rules['stringdivisible'] = params;
    options.messages['stringdivisible'] = options.message;
});

This code will match the divisor passed as a parameter in the ValidationParmeters to the params option and enable the rule used for the actual validation. If you don’t have any parameters to your function, pass true to options.rules[‘stringdivisible’].

You also need to add the actual method that will perform the validation

jQuery.validator.addMethod('stringdivisible', function (value, element, param) {
    if ((value.length % param.divisor) == 0)
        return true;
    return false;
}, '');

There you have it. That should be the total amount of work needed to make things run on the client side. Give it a try, hopefully most postback-only validation might be a thing of the past.

Advertisements
Tagged , ,

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

%d bloggers like this: