Friday, 27 August 2010

Custom MVC 2 validation using jQuery – implementing client side validation in addition to server side

When I was looking for information on wiring up custom validation in MVC 2 I couldn’t find a lot of information on getting the client side stuff working with jQuery that a) didn’t involve manually changing the MicrosoftMvcJQueryValidation.js file or b) worked, so I set about working it out myself. Here is what I found – a walk through for getting server and client side validation working using jQuery.

See my earlier post on getting validation working with AJAX loaded forms too.

Ok, so first, go off and read this guide from Phil Haack, this is the groundwork you need to get validation working, which I’ll briefly re-iterate here before talking about getting custom jQuery validator wired up.

Stage 1 – getting validation working on the server side first.

1A: your custom validation attribute

The following is a basic skeleton for a validator that will check a string’s minimum length (yes I know we have validators for min and max length, I’m trying to be concise here and show how to roll your own!);

public sealed class MinimumLengthAttribute : ValidationAttribute
    public int MinimumLength{ get; private set; }

    /// <remarks/>
    public MinimumLengthAttribute( int minimumLength )
        MinimumLength = minimumLength;

    /// <remarks/>
    public override bool IsValid(object value)
        if( value == null ) return false;

        string text = (string) value;
        if( text.Length < MinimumLength ) return false;

        return true;
1B: Consume your validation attribute in your view model

Mark up your target model property with your validation attribute.

public class AddUserViewModel
    [MinimumLength(6, ErrorMessage="Password must be specified and be at least 6 characters long")]
    public string PasswordOne{ get; set; }
1C: Output some validation messages

Use the MVC ValidationMessageFor helpers to output some validation messages.

<%=Html.LabelFor( m => m.PasswordOne) %>
<%=Html.EditorFor( m => m.PasswordOne )%>
<%=Html.ValidationMessageFor( m => m.PasswordOne) %>
1D: Check the model state in your controller

When your model is posted into your controller action, it will be automatically validated. You can check the model state and act accordingly, something like;

if (!ModelState.IsValid)
    return View(userModel);

That’s it for server side stuff, if you fire up your form, leave the field blank and then submit it, the error message will appear. For the client side stuff we need to go a little further;

Stage 2 – get client validation working on the client

2A: Include validation base scripts

Include the following in your page to include the jQuery validation stuff. You may be asking, “where is MicrosoftMvcJQueryValidation.js, I don’t seem to have it” – it’s presently part of the MvcFutures project – take a look on codeplex.

<script type="text/javascript" src="/Scripts/jquery.validate.min.js"></script>
<script type="text/javascript" src="/Scripts/MicrosoftMvcJQueryValidation.js"></script>

2B: Tell your form to output client validation information

This must be called BEFORE your Html.BeginForm – it tells the view context to output validation information in a script when the form is disposed. This doesn’t actually DO any validation, it just outputs the appropriate javascript data to tell your chosen engine what rules need to be implemented. The validation is actually wired up by a piece of javascript wired into the document.ready event from the MicrosoftMvcJQueryValidation.js file – again, if you’re loading your form using AJAX, your validation won’t get wired up, you need to take extra steps….

2C: Wiring up some client side code to the custom validation attribute

We now need to write some code that outputs the appropriate javascript data (at the end of the form) for our custom client validator, once we write it.

public class MinimumLengthValidator : DataAnnotationsModelValidator<MinimumLengthAttribute>
    private readonly int _mininumlength;
    private readonly string _message;

    public MinimumLengthValidator( ModelMetadata metadata, ControllerContext context, MinimumLengthAttribute attribute ) : base(metadata, context, attribute)
        _mininumlength = attribute.MinimumLength;
        _message = attribute.ErrorMessage;

    public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
        var rule = new ModelClientValidationRule
            ErrorMessage = _message,
            ValidationType = "tj-custom"
        rule.ValidationParameters.Add("minparam", _mininumlength);

        return new[] { rule };

Notice we don’t add the code to the validator attribute. That’s because the validator attributes aren’t MVC specific, you can use those in other technologies too, so adding MVC specific guff to those attributes would have been quite a pollution – instead the wrapper above takes an instance of the MinimumLengthAttribute we’ve defined in it’s constructor and then sets local members that we want to use on the client. The GetClientValidationRules() override then specifies what will be output in the javascript validation rules on the client – the basic stuff is the ErrorMessage which we pass through from the validator and the ValidationType which tells the validation stuff on the client what type of validation to execute (in this case it’s our custom validator which we need to setup called tj-custom). ValidationParameters is then used to build up any parameters we want to pass into our validator.

2D: Telling MVC that the above validator is the client side adaptor for our minimum length attribute;

We now need to tell MVC that when it comes across our validator (our MinimumLengthAttribute) it should use the MinimumLengthValidator class to generate the javascript rules for the client. We do this during Application_Start with the following code;

DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(MinimumLengthAttribute), typeof(MinimumLengthValidator));
2E: Registering our new client validation function

The final step is to actually write our new jQuery validator and register it with jQuery. (Now I think about it, I guess all the above steps apply to any client side validation technology you want to use and only this last step would be different!).

Remember in the ModelClientValidationRule we’re returning from our validator adapter above, we specified a validation type of “tj-custom”. We register a handler for this as follows;

jQuery.validator.addMethod("tj-custom", function (value, element, params)
    if (value == null) return false;
    if (value.length < params.minparam) return false;
    return true;

Notice the params structure is a mirror of the params we returned in the ValidationParameter from the adapter? All we do is check the value against the params and return true if validation is passed or false if it’s failed. Simple as that – no need to start messing around with the jQuery in the MicrosoftMvcJQueryValidation file, basically, if the mvc built in stuff doesn’t recognise the validation type it passes it through to __MVC_ApplyValidator_Unknown method which just passes data through to our code using the pattern above.

Disclaimer :)

I’ve unpicked this from the code I’m working on so I may have missed something minor, comment me if you have any questions. Enjoy……

1 comment:

  1. Source code to demonstrate this here;