Setting up jQuery Unobtrusive Validation

Wednesday, May 20, 2015

I love jQuery Unobtrusive Validation. It allows me to include some script on a web site, add some attributes to my forms and presto: I have configurable validation. Wonderful.

Except that I end up fiddling for at least half an hour every time I try to add it to a site. The setup is really simple, but I keep forgetting how it works, exactly, and I haven't found a decent quick start guide yet. So here's one.

TL;DR: find the result in this JSFiddle

1. Include the right scripts

The heavy lifting is done by the jQuery Validation plugin, which depends on (you guessed it) jQuery. In order for the whole thing to become unobtrusive, you need jQuery Unobtrusive Validation. So all in all you need:

<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.min.js"></script>
<script src="//ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>

Choose your favorite version numbers and CDN's, but these are the required scripts (in this order!)

2. Mark elements for validation

Unobtrusive validation is called just that (unobtrusive) because once the scripts are loaded, it's entirely attribute-based. The form tag itself can stay as it is, but validation has to be added to every input element that needs to be validated.

Here's how you set it up per element:

  1. Add the attribute data-val="true" to it which enables validation on the element 
  2. Add one or more validation attributes, such as data-val-required or data-val-min. See below for a list of possible attributes

This will cause the unobtrusive validator to perform its magic, which basically consists of juggling css styles on these elements. Here's a text input with validation:

<input type="text" name="color" data-val="true" data-val-required="Please enter a color" />

Here's a list of the validation attributes you can use (obtained here):

  • data-val-required="message". Shows the message when the element has no value
  • data-val-length="message" + data-val-length-min="min length" and/or data-val-length-max="max length". Shows the message if the contents of the element are too long or too short
  • data-val-number="message" or data-val-date="message". Shows the message when the value of the element is not of the right type. Other types: data-val-creditcard, data-val-digits, data-val-email, data-val-ur 
  • data-val-regex="message" + data-val-regex-pattern="^pattern$". Shows the message if the value of the element does not match the pattern
  • data-val-equalto="message" + data-val-equalto-other="jQuery selector". Shows the message if the contents of the element are not the same as the contents of the element selected by the jQuery selector (usually "#someid")
  • data-val-range="message" + data-val-range-min="min" + data-val-range-max="max". Shows the message if the contents of the element are not in the range. Specify both min and max!

3. Displaying validation messages

Of course, the validation results (which are in fact validation error messages) have to appear somewhere - and you have to create elements for that.

There are two ways to display validation errors 

  • A validation error message per element
  • A summary, displaying all validation errors

Adding validation per element

  1. Add a validation result element, that will show when validation fails. This can be any element, normally a div or a span 
  2. Give it the data-valmsg-for attribute. Set the value of the attribute to the name (not the id!) of the element it displays a result for
  3. Add data-valmsg-replace="true" if you want the contents of the result element to be set to the validation error when one occurs. Normally, this should be the case. If not, the contents will be left alone, so the won't be informative

The color text box in the previous sample could have the following validation result element:

<span data-valmsg-for="color" data-valmsg-replace="true"></span>

Adding a validation summary

  1. Add an element with data-valmsg-summary="true" to the form. Make sure it's inside the form!
  2. Important: Give this element an (empty) ul-element. This element will receive li-elements for every validation error that occurs. Without it, the validation summary will not work

This is how a simple validation summary element might look:

<div data-valmsg-summary="true">
 <ul></ul>
</div>

Validation summaries have a big drawback though: their contents are not updated when the validity of elements changes while tabbing around.

4. Style it

Unobtrusive validation works by setting and removing attributes on the element to be validated, and additionally changing the contents of the elements used to display validation results.

  • Validated elements (marked with data-val) get one of the class names input-validation-error or valid added to them, depending on their validity. Additionally, a bunch of aria-attributes get added and removed
  • The validation result elements (those marked with data-valmsg-for) get the class name field-validation-error or field-validation-valid
  • The validation summary element (marked with data-valmsg-for) gets the class name validation-summary-errors added when it contains validation errors; if not, the class name is removed

So, to style you validated elements you could use:

.input-validation-error {
border: solid 1px red;
}

This would result in all elements that have failed validation (i.e. that are invalid now) to show a red border.

The validation error elements can be styled like this:

.field-validation-error {
color: red;
}

This makes all error messages associated with validated elements red.

Validation strategies

Validate-on-tab

The default unobtrusive validation strategy is to do nothing when the page is first loaded. Only when the user tabs away from an input element, it's validated for the first time. This is an excellent strategy that we're all used to: when the page is loaded, all form elements have their initial value - which will be invalid for most items, simply because the user hasn't entered anything into them yet. But because he has had no opportunity to do anything about that, we're not going to bother him with the results.

Using this strategy, we have to make sure the individual validation result elements are in the right "state": they must appear to be valid. This can easily be achieved by giving them the field-validation-valid class. Alternatively, you can rely on the fact that your validation result elements will be empty, and therefore not take up any space. In that case there's no need for action.

Start validation on page load

Occasionally, this is not enough. Sometimes you want the form to show which elements are invalid as soon as the page is loaded. Imagine a user's profile is incomplete - he needs to enter an email address. In that case, we can display his profile with all data available and show the email text input as invalid only if it's still empty. Do this with a startup script like this, which calls valid() on all forms:

<script>$(function() {
$("form").valid();
});
</script>

The selector used (in this case form) determines which forms are validated - in this case, all forms. This will also populate any validation summaries!

A simple working jQuery Unobtrusive Validation Example

I've made a JSFiddle with a simple form with validation. It asks for a name and demands you provide one, and that it's longer than two letters. Also, you must enter your age, which must lie between 10 and 120.

Happy validating! Unobtrusively, of course.