NAME Form.Validator - a framework for client-side form validation SYNOPSIS First, make a form with an "onsubmit" handler:
name:
category:
cost:
Then, define the reporting method and your constraints: At any time, you can change the way validation is reported: Cliff's Notes: Learn to use set(), reporter(), and validate(). Demo Site: http://asciicker.kicks-ass.org/x/form_validator_demo.html DESCRIPTION Form.Validator provides a framework for form validation that allows one to concisely define validation constraints. The readability of the code you have to write was of prime concern, and tangential to that was the notion of keeping typing to a reasonable minimum. The goal was to provide an expressive API that would be easy to read as well as write, requiring the programmer to only learn a few methods to do his work. The method that you will be most concerned with will be "set()". It is through "set()" that input fields will be associated with constraint functions, and you'll call this method repeatedly for each constraint you want to define. (There is a lot of opportunity for vertical alignment of parameters for those of you who are ASCII-artistically inclined.) The current collection of validation functions and function generators is fairly small, but they should be expressive enough for almost any need. Learn them well, but when they're just not enough, know that you can always write your own validation functions to test anything else you need. Finally, one of the most intersting features of Form.Validator is that you can choose different ways to report validation failure. Using the "reporter()" method, you can tell a form validator to 'AlertAll' which means display an alert pop-up that shows all validation failures upon unsuccessful validation. If you want to be more modern, you can use 'InnerHTML'. This makes it so that you can see error messages being updated in divs or spans as you type into the form. (For real world examples of this technique, see "INSPIRATION".) What's nice about this approach is that changing between different reporting mechanisms is a fairly effortless task for the programmer. This was the result of having a clean separation between the code that does validation and the code that does error reporting. The end result is a framework for form validation that (I hope) will help a lot of programmers with their client-side validation needs. METHODS new Form.Validator(form) This is the constructor. It takes a form object as its only parameter. Example: var fv = new Form.Validator(document.stuff); var fv2 = new Form.Validator(document.forms[0]); set(field, constraint, option); This method associates a form field with a validation function and an error message. The first parameter is the name of a form field. The second parameter is either a string with the name of a built-in validation function (like "notBlank" or "notZero"), or an actual function that can perform a validation test. The third parameter is either a string that will be used when the validation fails or an object with properties that will be used by a report object to display validation results. Form.Validator.Report.InnerHTML is currently the only class that will take advantage of this. The standard properties for the object form of the 3rd parameter are as follows: The constraint object's standard properties onFailure Message to use when validation fails. (Internally, the 3rd parameter gets assigned to onFailure when it's passed in as a string.) onSuccess Message to use when validation succeeds. (InnerHTML will use this if you provide it.) constraint This is the function used for testing a field. It should be able to take a Form.Validator object and a field name as parameters. (Internally, the 2nd parameter is assigned to constraint.) field This is the name of the field that is being checked. (Internally, the first paramter is assigned to field.) Example: The standard way. // using a built-in validator function fv.set('name', 'notBlank', "Name must be filled in."); Example: Using a custom validation function. // using a custom validator fv.set('category_id', function(fv, field) { if (fv.form[field].value == 3) { return false; } else { return true; } }, "Problems always come in 3s. Bad choice, my friend." ); Example: Passing an object in the third parameter fv.reporter('InnerHTML'); fv.set( 'email', 'notBlank', { onFailure: 'Please fill in an email address', onSuccess: 'OK' } ); Note that the following 2 statements are equivalent: fv.set('x', 'notBlank', { onFailure: 'X is required' }); fv.set('x', fv.notBlank, 'X is required'); get() get(field) This method returns a list of the constraints for a given input field. If a field is not passed in, this method will return all constraints in the order that they were defined. Example: var name_constraints = fv.get('name'); // constraints for name var all_constraints = fv.get(); // Array of all constraints reporter(report) This method allows one to specify the way validation errors will be reported back to the user. Your choices are as follows: AlertAll This is the classical approach where all validation messages are joined together and displayed in an alert pop-up. See Form.Validator.Report.AlertAll. AlertFirst This will stop on the first validation error and display an alert pop-up. After the pop-up is closed, it will focus on the form element where the validation error occured. See Form.Validator.Report.AlertFirst. (I affectionately refer to this as Indian-style validation.) InnerHTML This was inspired by the way http://www.remeberthemilk.com/ handles form validation during user registration. If you try to create an account on that site, you'll see that it validates the form as you type, and validation messages are placed in the innerHTML of an element right next to the form input it references. I was so impressed by that validation technique that I rearchitected Form.Validator so that it could accomodate multiple validation styles with this particular style in the forefront of my thoughts. See Form.Validator.Report.InnerHTML. Example: // You can change the reporting method at any time. fv.reporter('AlertFirst'); fv.validate(); fv.reporter('AlertAll'); fv.validate(); Note that you can also pass an object to the "reporter()" method. The minimum requirement is that the object support a start(), run() and finish() method. Example: fv.reporter(new Form.Validator.Report.AlertFirst(fv)); This is effectively the same as: fv.reporter('AlertFirst'); validate() validate(field) This method runs through the list of constraints in the order they were defined. It also executes the "start()", "run()", and "finish()" methods of the report object at the appropriate times. If any of the constraints failed, this method will return false. Otherwise, it'll return true. Example: Validating a form all at once
... ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Example: Also validating one field at a time
validateAndAlert() *** DEPRECATED *** This function is intended to be used as an "onsubmit" handler on a form. It runs through the validation tests in the order they were defined and displays an alert box if any of the validation tests failed. Then it will return "false" to prevent the form from being submitted. Example:
BUILT-IN VALIDATION FUNCTIONS notBlank(fv, field) For inputs that shouldn't be blank, use this test function. notZero(fv, field) For inputs that shouldn't be 0, use this test function. VALIDATION FUNCTION GENERATORS makeLengthMin(number) This method returns a function that will ensure that the contents of a field are at least n characters long. Example: fv.set( 'name', fv.makeLengthMin(3), "The name field must be at least 3 characters long" ); makeLengthMax(number) This method returns a function that will ensure that the contents of a field are not more than n characters long. Example: fv.set( 'name', fv.makeLengthMax(10), "The name field may not be more than 10 characters long" ); makeValueMin(number) This method returns a function that will ensure that the value of a field is at least n. makeValueMax(number) This method returns a function that will ensure that the value of a field is less than or equal to n. makeMatchRegex(regex) This method returns a function that will match the regex passed to it. Example: fv.set( 'country', fv.makeMatchRegex(/CA|US/), "Sorry, we do not ship outside of Canada or the US." ); makeNotMatchRegex(regex) This method returns a function that will make sure that the specified input does not match the given regex. Example: fv.set( 'do_i_look_fat_in_this', fv.makeNotMatchRegex(/[Yy]es/), "I hate you!" ); makeOptional(funktion) Sometimes, you only want to check a constraint if something was typed in. That's when you'd use this method. Example: var naive_email_regex = /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/; fv.set( 'email', fv.makeOptional(fv.makeMatchRegex(naive_email_regex)), 'If you're going to type in an email address, type it right!' ); Thus, you can wrap any function with fv.makeOptional to make it an optional constraint. (Optional is defined as "check if the field is not blank".) WRITING YOUR OWN VALIDATION FUNCTIONS If the provided validation tests cannot fulfill your needs, you may add validation functions of your own. They should expect to receive 2 parameters. The first is the Form.Validator object (fv) and the second is the name of the field that the function needs to test. To get at the actual form, access "fv.form". Example: function notOdd (fv, field) { var form = fv.form; if (form.field.value > 0 && form.field.value & 0x01) { return false; } else { return true; } }; fv.set('number', notOdd, "Only positive, even numbers are allowed."); Furthermore, if you feel compelled to add this method to the Form.Validator class, you can do this: Form.Validator.prototype.notOdd = notOdd; Now that we've added the "notOdd" method, all Form.Validator objects can test to see whether a field has an odd value or not. WRITING YOUR OWN VALIDATION REPORTERS Currently, you get to pick from Form.Validator.Report.AlertAll, Form.Validator.Report.AlertFirst, and Form.Validator.Report.InnerHTML as a way to report what went wrong. If you want to implement your own way of reporting that works within the Form.Validator framework, here are the methods your object needs to implement. The Constructor The constructor will be passed a Form.Validator object with which it can do whatever it wants. It can be useful, so keeping it in a property for later use is recommended. "start(constraints)" Before validation starts, this method will be called with a list of constraints that will be checked in this pass. You can use this opportunity to perform initializaton. "run(ok, constraint)" Implement a method that takes a boolean value and a constraint object. The boolean value will contain the result of the constraint function. If it's false, that means the constraint failed, so you'll want to update some data structures so that you'll be able to display an error message. "finish()" The finish method is executed after all the validations have been performed. If you haven't done so already, now would be a good time to actually display any messages concerning validation failures. If this object prototype is placed within the Form.Validator.Report.* namespace, the "reporter()" method will be able to see it. For example, if you created something called Form.Validator.Report.SVG, you could tell your Form.Validator object to use it by saying: fv.reporter('SVG'); If, for whatever reason, you don't want to put your reporter in the Form.Validator.Report.* namespace, that's fine. As long as you support the "start()", "run()", and "finish()" methods, it will work. The only difference is that you'll have to call "reporter()" a little differently. For example, if you created a report object prototype called My.Brilliant.Report, you'd make the validator use it by saying: fv.reporter( new My.Brilliant.Report() ); INSPIRATION Originally, I wanted to avoid repeatedly writing the same javascript over and over again for form validation. I'm satisfied with how I tackled that problem, but new developments in form validation techniques have pushed me to improve this library further. To that end, I am indebted to the following implementations of form validation for their inspiration. http://rememberthemilk.com/signup/ This is the first implementation of the innerHTML validation technique that I ever saw. My friend, Jeremy Seitz, brought this to my attention for the signup page alone, and I must agree with him that it is quite impressive how they validate your input as you type, even making AJAX requests for some of their constraint checking. If I hadn't seen this, I would not have been motivated to rearchitect Form.Validator. http://masuidrive.jp/validation/index_en.html This is a more general implementation of the innerHTML validation technique that uses the Behaviour library in a clever way. (However, It would have been nicer if he didn't assimilate the code of behaviour.js into his own library.) TO DO * Add more validation functions. Surprisingly, "notBlank" and "notZero" alone can take you pretty far, but a few more validation functions would be nice to have. * Be able to handle checkboxes and radio buttons Perhaps a validation function called "isChecked()" could be written. * Add a validation function named, 'isRequired' This would subsume "notBlank" and "notZero". AUTHOR John Beppu (beppu@cpan.org)