More than Meets the Eye: Form Validator

Written By Aaron Newton, on Friday, April 30th 2010, 3:09pm

Continuing with my “More than Meets the Eye” series, today I want to talk to you about the MooTools More Form.Validator. There was a comment left on my last post in this series (about Form.Request) specifically requesting that I cover this relatively complex plugin that’s useful for telling users about the validity of data they enter into forms before they send them.

Getting Started with Validators

The main class you need to concern yourself with is the Form.Valdiator class itself which provides methods that apply specific rules to inputs to see if they are valid. You can then choose how you want to inform your users that they need to address these problems, but that’s not the job to Form.Validator (though it is the job of Form.Validator.Inline, which we’ll cover in a bit).

Let’s talk little bit about the rules that are applied. Form.Validator allows you to define rules that test the state of an input and return true or false - true if the input passes the validation rule, and false if it doesn’t. Here’s a simple example:

Form.Validator.add('doesNotContainTheLetterQ', {
        errorMsg: 'This field cannot contain the letter Q!',
        test: function(field){
                return !field.get('value').test(/q/,'i');
        }
});

The above code adds a global validator that allows you to assert that the input doesn’t use the letter Q. The arguments are the validators’ key and then an object that contains an error message to show the user should they encounter the error, and a function that is passed the input as an argument. This function inspects the value or, really, anything you like, and then returns true or false.

The key you give your validator is important. At any time you can validate a field against any validator by using this key as a reference by doing:

myFormValidator.test(key, input[, warnOnly]);

The first two arguments are the important ones here; the key of your validator and the input to test. Where things get interesting are when Form.Validator does this for you. By giving your input that key as a css class name, you tell Form.Validator to validate that field with this validator. In this manner you can quickly and unobtrusively decorate your inputs with the requirements and validate the form. If something goes wrong with your JavaScript, your form will submit as normal. Here’s a really simple example:

The alerts aren’t pretty, but you can see how our validator is now applied when you submit the form.

Form.Validator ships with several validators listed in the documentation. These include simple stuff like required that just validates that the user put something - anything - in the input. But there are also validators for email addresses, only letters, dates, urls, etc. The key is you can write your own - you don’t need to wait for us to release something for you to make use of it. There are an extended list of validators in Form.Validator.Extras that include some edge cases. Things like validating that two inputs have the same value (like an email verification for example).

Using Validators with Arguments

It’s also possible to configure validators with data unique to the input. For example, let’s say you want to have an input with a minimum length validator - the user must type in at least 5 characters. You could write a validator called minimum5 or whatever, but then you’d have to duplicate it for any other character length. For this purpose, Form.Validator allows you to assign values to the input, like so:

<input type="text" name="username" class="minLength:10" id="username"/>

These values - the 10 and 100 values in the example above - get passed along as JSON decoded values to the validator. Here’s what that looks like:

Form.Validator.add('minLength', {
    errorMsg: function(element, props){
        return 'Please enter at least {minLength} characters (you entered {length} characters).'.substitute({minLength:props.minLength,length:element.get('value').length });
    },
    test: function(element, props){
        if (props.minLength != null) return (element.get('value').length >= props.minLength;
        else return true;
    }
});

As you can see, the error message (which in our previous validator was just a string - the message) can also be a function which is passed the input and then any properties defined in the HTML. The fact that the message can be a function allows you to include information not only about how the validator is configured but also other information, like some aspect of the value the user actually entered. The test is also passed along these p roperties of course which allows you to make the properties a condition of the test. We could, in theory, rewrite our doesNotContainTheLetterQ validator to accept an argument about the character (or, better yet, characters) that we want to disallow:

Form.Validator.add('doesNotContain', {
    errorMsg: function(field, props){
        return 'The value you input cannot contain any of the following letters: ' + props.doesNotContain;
    },
    test: function(field, props){
        if (props.doesNotContain)
            return !field.get('value').match(new RegExp('[' + props.doesNotContain + ']', 'i'));
        else return true;
    }
});

Note that the properties defined have to be JSON decodable, so you can’t have your input like this:

<input type="text" class="doesNotContain:qz"/>

Instead you’d have to quote the string:

<input type="text" class="doesNotContain:'qz'"/>

Here’s our example:

The base Form.Validator class comes with a plethora of options and events. You can configure it to validate inputs whenever they change (on blur), or to only show one error at a time (serial). You can tell it to ignore hidden fields (those that are not visible, there’s no point in validating inputs with type=”hidden”) and disabled inputs. There are numerous useful methods that let you run the entire validation routine on the form (.validate()), reset all the errors (.reset()) or validate / reset a specific field (.validateField() and .resetField()). You can pause the validator and resume it (.stop() and .start()) as well as ignore / un-ignore a specific field (.ignoreField() and .enforceField()) and more. There’s even an element method that lets you validate a form (Element.prototype.validate).

Form.Validator.Inline - the “Pretty” One

Now that we’ve covered the basics of how Form.Validator works, let’s consider the fact that our examples so far have been rather ugly (who wants alert messages?). MooTools More ships with a default implementation that smoothly displays messages inline, right after the input: Form.Validator.Inline. This class is the “pretty” version of Form.Validator but don’t think of it as the only game in town. You can easily implement your own “pretty” version without a lot of effort. If you want to put errors in a popup or fade them in over the entire screen or play a sound it doesn’t matter. The base Form.Validator class is there for you.

Looking at the Form.Validator.Inline implementation, you’ll find all the same options and methods from Form.Validator along with a few extras that control how your messages appear. For instance, by default, the validation rules show up immediately after the input. This requires a bit of forethought in how you structure your HTML. If your input is inline with something else (like a submit button), validation errors are going to change that when they appear (because they are divs, which by default are block level elements).

The only real option that Form.Validator.Inline has to offer is whether or not to scroll to errors (useful if your form is long and will likely cause the user to scroll to the submit button). Here’s a simple example, building on our previous one:

As you can see, the error message slides in, but it breaks our layout a bit. Let’s do some CSS work to make this look a little nicer.

It’s not The Sistine Chapel but it does look nicer. Our error doesn’t appear to break our layout anymore either.

As a final example, I’ll show you a version that extends Form.Validator.Inline to put the validation messages in tips that popup pointing at the input. Here’s a demo:

If you want you can check out the source on github and download it with its dependencies on Clientcide.

Go Forth and Validate

Overall the purpose of Form.Validator is not to be the only solution for displaying validation messages to your users, but rather a flexible solution for client side form validation on which to build. You can extend it and manipulate it to display messages in any way you choose - and you should!. The default “pretty” implementation - Form.Validator.Inline - is just one way to use the class. In my opinion Form.Validator is one of the classes in MooTools More that shows off the power of MooTools’ object oriented design, allowing for a great deal of flexibility and reuse.

Thanks to Lloyd for suggesting this edition’s topic. If there’s a plugin in MooTools More you’d like me to focus on next time, by all means, make a suggestion in the comments.

Aaron Newton is a contributor to MooTools and the principal developer of MooTools More. He is the author of the book MooTools Essentials as well as the Mootorial online MooTools tutorial. He posts (rarely these days) at his blog Clientcide.com and (much more often) on Twitter as anutron. He works for Cloudera, (which is hiring, by the way).

5 Responses to “More than Meets the Eye: Form Validator”

  1. Jesse Donat says:

    Super useful. I’ve never given the validator a chance, but now I feel a strong urge to modify my PHP validator in a way that they could share creation with MooTools validation.

  2. Lloyd says:

    Thanks again Aaron! Really appreciate the article with examples (Form.Validator, Form.Validator.Inline and Tips). I look forward to the next post.

  3. Matt Puchlerz says:

    I really like the API for adding new validations. That said, the manner in which you can specify validator arguments within a class name feels really hackish, for several reasons:

    • The class name itself—while workable for our purposes here—becomes completely invalid.
    • You lose the ability to style inputs based on their validation.
    • Trying to retrofit loose JSON syntax within the value of an attribute with a known purpose is a code smell.

    Instead, what if you were to utilize non-descript data attributes, valid in HTML5? Perhaps you could leave the validator key as a class, and pass any arguments via a data attribute of the same name:

    <form id="myform">
        <input type="text" class="doesNotContain" data-doesNotContain="qz">
        <input type="submit">
    </form>

    Then again, if you are concerned about current XHTML validity, the data attributes may not be the way to go. In that case you might be able to get away with splitting up the class name with valid syntax:

    <form id="myform">
        <input type="text" class="doesNotContain-qz" />
        <input type="submit" />
    </form>

    Yet at this point you would be limiting arguments to a string. I’m sure it was this consequence which led you to the loose JSON format to begin with.

    Either way, my preference would be for the data attributes. They separate out the concerns, won’t run the risk of invalidating your class names, and are valid in the markup language that you’ll probably using soon, if not already.

  4. aaron says:

    Hey Matt,

    This very thing has been on my to-do list for the last few weeks. The data attribute thing specifically. Look for this change in future releases of the plugin (we’ll maintain backwards compatibility of course). If you wanted to, you get help out; just fork on github, implement the new features, and send a pull request. If not, that’s cool. We’ll get to it soon enough.

    Aaron

  5. Matthew Fedder says:

    Looking at the examples, it seems like these validators only provide results that are useful for sighted users. Nothing demolishes your site’s usability to unsighted users faster than having forms that do not submit.

    It sounds like it should be reasonable enough to implement two things that would aid accessibility:

    a) When clicking submit and validate fails, give focus to either a list of the validation errors, or the first validation error b) Ensure that all validation errors are placed in the for a field

    If I get a chance to implement this for one of my sites, I’ll post the results…. somewhere or another.