MVC 4.0: How to support client-side custom validation

mvcI talked about CUSTOM VALIDATION LOGIC  and we saw how to implement custom validation logic to our model classes.

Here we will see how to support client-side validation for our custom validation attribute.

I will start with the solution then I will explain it in details.

    public class MaxWordsAttribute : ValidationAttribute,
                                     IClientValidatable
    {
        public MaxWordsAttribute(int wordCount)
            :base("Too many words in {0}")
        {
            WordCount = wordCount;
        }

        public int WordCount { get; set; }

        protected override ValidationResult IsValid(
            object value,
            ValidationContext validationContext)
        {
            if (value != null)
            {
                var wordCount = value.ToString().Split(' ').Length;
                if (wordCount > WordCount)
                {
                    return new ValidationResult(
                        FormatErrorMessage(validationContext.DisplayName)
                    );
                }
            }
            return ValidationResult.Success;
        }

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
            ModelMetadata metadata, ControllerContext context)
        {
            var rule = new ModelClientValidationRule();
            rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
            rule.ValidationParameters.Add("wordcount", WordCount);
            rule.ValidationType = "maxwords";
            yield return rule;
        }
    }

To support client-side validation, you need your attribute to implement an interface.
The IClientValidatable interface defines a single method: GetClientValidationRules.
When the MVC framework finds a validation object with this interface present, it invokes GetClientValidationRules to retrieve — you guessed it — a sequence of ModelClientValidationRule objects. These objects carry the metadata, or the rules, the framework sends to the client.

If you think about the scenario, there are a few pieces of information you’d need on the client to run the validation:

  • What error message to display if the validation fails (Error Message)
  • How many words are allowed (Validation Parameters)
  • An identifi er for a piece of JavaScript code that can count the words (ValidationType)

The MVC framework takes the rules given back from the GetClientValidationRules method and serializes the information into data dash attributes on the client:

<input
data-val="true"
data-val-length="The field Title must be a string with a maximum length of 160."
data-val-length-max="160"

data-val-maxwords="Too many words in Title"
data-val-maxwords-wordcount="10"

data-val-required="An Album Title is required" id="Title" name="Title"
type="text" value="For Those About To Rock We Salute You" />

Now you have metadata on the client, but you still need to write some script code to execute the validation logic.

You’ll need two pieces of script in place for validation to work:

  • The adapter: The adapter works with the unobtrusive MVC extensions to identify the required metadata. The unobtrusive extensions then take care of retrieving the values from data dash attributes and adapting the data to a format jQuery validation can understand.
  • The validation rule itself: This is called a validator in jQuery parlance.
/// <reference path="jquery-1.6.2.js" />
/// <reference path="jquery.validate.js" />
/// <reference path="jquery.validate.unobtrusive.js" />

if ($.validator && $.validator.unobtrusive) {

    $.validator.unobtrusive.adapters.addSingleVal("maxwords", "wordcount");

    $.validator.addMethod("maxwords", function (value, element, maxwords) {
        if (value) {
            if (value.split(' ').length > maxwords) {
                return false;
            }
        }
        return true;
    });

}

You have four kinds of Adapter Methods (addBool, addSingleValue, addMinMax, add). For the maximum words scenario, you could use either addSingleVal or addMinMax (or add, because it can do anything).

The validator method takes two parameters:

  • The name of the validator, which by convention matches the name of the adapter (which matches the ValidationType property on the server).
  • A function to invoke when validation occurs.

The validator function accepts three parameters and can return true (validation passed) or false (validation failed):

  • The fi rst parameter to the function will contain the input value (like the title of an album).
  • The second parameter is the input element containing the value to validate (in case the value itself doesn’t provide enough information).
  • The third parameter will contain all the validation parameters in an array, or in this case, the single validation parameter (the maximum number of words).

Thank You,
Keep It Simple & Straightforward 🙂
~:H}{H:~