Regula: An annotation-based form-validator written in Javascript

by vivin

Regula is an annotation-based form-validation framework written in Javascript. There already exist a few frameworks that address form-validation in Javascript, but I have found them to be somewhat lacking. I have thought about writing one of my own for some time, but I honestly had no idea what form it would or should take. I knew that I wanted to make one that was easy to use, flexible, and easily extensible (custom validation rules). I finally got an idea as to the form my framework should take, when I was looking at Hibernate bean-validation. I like the fact that you can set constraints by using annotations like @NotNull or @NotEmpty. That way, when you look at the bean, you are immediately aware of the constraints attached to it. I wanted to do something similar in HTML.

Enter HTML5. HTML5 supports and endorses custom attributes in HTML tags. Purists may scream “No!”, but I think it is a useful feature. However, like all features it has the potential for abuse. But that’s another topic entirely. Anyway, I started thinking – what if I could use a custom attribute to specify the constraints? Then I could use Javascript to identify those constraints and then enforce them during validation. What I was thinking of, was to do something like this:

<input id = "myInput" 
         type = "text" 
         data-constraints = '@NotEmpty @IsNumeric @Between(min=1, max=5)'>

That bit of code describes a text box which cannot be empty, and which expects a numeric value between 1 and 5. With this basic design in mind, I started working on my framework. It took me about a week of on-and-off work (maybe 3 days actual work) but I’ve come up with something that is, in my humble opinion, a flexible and easy to use framework. I plan to document the use of this library/framework more thoroughly, but this post should serve as a gentle introduction to the framework and its features.

In Regula (which, by the way, means “rule” in Latin) form elements (input elements or even the form elements) can have constraints attached to them in a similar fashion to the example I described earlier. There is a small difference, however:

<input id = "myInput" 
         class = "regula-validation"
         type = "text" 
         data-constraints = '@NotEmpty @IsNumeric @Between(min=1, max=5)'>

As you can see, I use a class name of regula-validation which tells the framework that this input element has constraints attached to it. The reason I did this was for efficiency reasons. It’s much more efficient to grab all elements that have a class name of regula-validation than walking the whole document tree to search for nodes that have a data-constraints attribute.

After you annotate the input elements with their constraints, you’re halfway there already! All you have to do after that is add a script tag for the framework:

  <script type = "text/javascript" src="regula.js"></script>

And then add the necessary Javascript to validate the form (example uses jQuery):

jQuery(document).ready(function() {       
    // must call this first. The best place would be in an 
    // onload handler. This function looks for elements with
    // a class name of "regula-validation" and binds the 
    // appropriate constraints to the elements
    regula.bind(); 

    jQuery("#myForm").submit(function() {
        // this functions performs the actual validation
        var validationResults = regula.validate();

        for(var index in validationResults) {
             var validationResult = validationResults[index];
             alert(validationResult.message);
        }
    });
});

That’s pretty much all there is to it. More advanced uses of the framework don’t deviate that much from this pattern. Compared to other form-validation frameworks (that I’ve seen), validation will not stop on the first failing element. The validate function runs through every input element and validates it against the constraints bound to that element, and returns an array of “validation results”. Each “validation result” element in the array has the following properties:

  • constraintName – the name of the failing constraint
  • custom – a flag that says whether this is a custom constraint or not
  • constraintParameters – An array of objects that represents the parameters passed to this constraint (defined in the HTML. For example @Max(max=5), where you have a parameter who’s name is max and who’s value is 5. There’s a little more to this, but like I said, this is supposed to be a gentle introduction :))
  • receivedParameters – A hash (organized such that the name of the parameter is the key, and the value is well, the value) of parameters that the validator function received (helpful when you have a custom validator).
  • failingElements – An array containing references to the actual input element or elements (in the case of form-specific constraints) that failed the constraint.
  • message – The error message.