Form Validation With Custom Modules In Dojo 1.7

  • Sharebar

In the last blog, DojoX form validation with custom error messages, I tried to explain how to validate a form with custom error messages using dojox.validate, but the drawback with that approach was that it was not modular. This blog is just an attempt to create your own custom form and error modules and use them to validate your form(s) without re-writing the validation code and doing things in Dojo way.

Overview

Defining modules in Asynchronous Module Definition (AMD) format helps the developers to develop the code and debug easily. The syntax for module identifier has changed in Dojo 1.7 from my.firstmodule.Rocks to my/firstmodule/Rocks, which also looks like actual paths too. Another thing to note is that a package contain modules so in our case 'my' is a package and firstmodule is a module. For detailed information, look "Dojo 1.7 Defining Modules", while you are at it please also look at the difference between Requiring Modules and Defining Modules.

Create custom form validation module

HTML

Firstly, let's create a very simple HTML that would contain an error container, a registration form and a login form:

<div id='errors'></div>
<form name='register' id='register-form' method='POST' action="/register/">
    <label for='remail'>Email</label><input type='text' value='' name='email' id='remail'/>
    <label for='rpassword'>Password</label><input type='password' value='' name='password' id='rpassword'/>
    <label for='rrepassword'>Re-type Password</label><input type='password' value='' name='repassword' id='rrepassword'/>
    <input type='submit' class='button' id='submit-register' value='Create my account' />
</form>
<form name='login' id='login-form' method='POST' action="/login/">
    <label for='lemail'>Email</label><input type='text' value='' name='email' id='lemail'/>
    <label for='lpassword'>Password</label><input type='password' value='' name='password' id='lpassword'/>
    <input type='submit' class='button' id='submit-login' value='Login' />
</form>

<!-- The contents of these two files are included at the end on this blog-->
<script type='text/javascript' src="app/resources/static/js/validateRegister.js'></script>
<script type='text/javascript' src="app/resources/static/js/validateLogin.js'></script>

Directory Structure

Next, create the following two modules in 'my' package:

static
   js/
     dijit/
     dojo/
     dojox/
     my/
        error/
            _base.js
        form/
            _base.js
            helper.js
        error.js
        form.js
     util/

form.js : is the actual module file
form/ : contain files for the form module
          _base.js : a class to represent a form
          helper.js : a helper class that attaches the onsubmit event to the current scoped form 
                      and other helper functions to print out the error messages

error.js: is the actual module file
error/ : contain files for the error module
         _base.js : a class to represent errors, provides a constructor and a function 
                    that prints out the form errors in the error container 
                    (which we provide during form set-up, more on this later)

It is completely up to you where you want to save your package or modules but you have to configure the Dojo loader to locate the modules correctly. The following shows how to enable you package so that the loader can locate your modules (read here to find out more about dojoConfig):

Configure Package

<script>
            var dojoConfig = {
                baseUrl: "'js/",
                tlmSiblingOfDojo: false,
                packages: [
                    { name: "dojo", location: "dojo" },
                    { name: "dijit", location: "dijit" },
                    { name: "dojox", location: "dojox" },
                    { name: "util", location: "util" },
                    { name: "my", location: "my" }
                ],
                async: true,
                aliases: [
                    ["ready", "dojo/ready"],
                    ["xvalidate", "dojox/validate"],
                    ["xcheck", "dojox/validate/check"],
                    ["xweb", "dojox/validate/web"],
                    ["dom","dojo/dom"],
                    ["connect","dojo/_base/connect"],
                    ["event","dojo/_base/event"],
                    ["declare", "dojo/_base/declare"],
                    ["lang", "dojo/_base/lang"],
                    ["dom-class", "dojo/dom-class"],
                ]
            };
</script>

In the package array, the name could be anything you like and the location should be the actual path to you package (baseUrl/location). From now on all our modules wiould be accessible via 'my', Eg: require["my/form"]..., dojo loader would look for form.js in the js/my/ directory.

Form Module

The my/form.js module file is a very simple file that just defines or loads the required form object ( define callback always return a value, unlike require ).

define(["./form/helper"], function(form){
    return form;
});

But the main juice lies within the helper.js file in the "my/form/" directory. The helper class is nothing but an interface to my/form/_base.js that also makes use of error module to print the errors in the error container when a form fails to get validated.

define(["lang", "declare", "./_base", "dom", "event", "connect", "../error/_base"], 
function(lang, declare, BaseFormValidator, dom, event, connect, myError){
    var FormValidator = declare(null, {
        form: null,
        profile: null,
        errorContainer: 'errors',
        baseFormValidator: null,

        constructor: function(/*Object*/ args){
            declare.safeMixin(this, args);
        },

        getErrorsAsHTMLList : function(errors){
            // print errors as list
            var html = "";
            if (errors){
                html = "<ul>";
                for(var key in errors){
                    html += "<li>"+errors[key]+"</li>";
                }
                html += "</ul>";
            }
            return html;
        },

        submitOrPrintErrors: function() {
            // validates form and if an error occurred then display the errors in the
            // error container
            var status = this.baseFormValidator.validateForm();
            if (status) {
                this.baseFormValidator.submitForm();
            } else {
                var html = this.getErrorsAsHTMLList(this.baseFormValidator.getErrors())
                var eObj = new my.Error({errorContainer: this.errorContainer, errorHTML: html});
                eObj.animateErrors();
            }
        }
    });

    var helper = lang.getObject("my.form.helper", true);

    helper.attachOnSubmitEvent = function(formId, profile, errorContainer){
        var f = dom.byId(formId);
        connect.connect(f, "onsubmit", function(e){
            event.stop(e);
            var baseFormValidator = new BaseFormValidator({form: this, profile: profile})
            var validator = new FormValidator({baseFormValidator: baseFormValidator, errorContainer:errorContainer});
            validator.submitOrPrintErrors();
        });
    }
    return helper;
});

PS: The modules names defined in the declare are 'aliases' to the actual modules, which we defined in the dataConfig above.

The helper class provides a single function 'attachOnSubmitEvent' that takes three parameters; a form id, form profile and the error container id. It then attaches the 'onsubmit' event to the given form and whenever our form is submitted the following actions are taken place:
1. Stop the default submit event
2. Create a new form object and pass the form and profile in the constructor
3. Create a new form validator object and pass the form object ( dependency injection ) as well as the id of the error container
4. Call the validator's 'submitOrPrintErrors' function

The main function that is doing all the dirty work is validator's 'submitOrPrintErrors'. Here is the dissect of the function:

1. Call the validateForm function of the BaseFormValidator object ( refer to the declare section or the very first line of helper.js file )
2. If form validated without any error then submit the form
3. Otherwise, get the form errors and transform those into an un-ordered list
4. Then create a new Error object and pass the error container id and the unordered list
5. Call the animateErrors function to display the errors ( currently no animation is applied to the error container but you can override this function in my/error/_base.js )

The base form class (my/form/_base.js) is nothing but a very simple Dojo class that has a constructor, getters and the validate function, which I already explained in my previous blog. I have attached the complete package for your reference, please have a look at the base form file in the attachment.

PS: helper.js returns the 'helper' object, which is then returned by the form module 'my/form.js' file.

Error Module

If you understood the Form module then Error module is even simpler to understand.

The module file (my/error.js) :

define(["./form/_base"], function(error){
    return error;
});

and the base error class (my/error/_base..js):

define(['declare', 'dom', 'dom-class'], function(declare, dom, domClass){
    return declare("my.Error", null, {
        errorContainer : 'errors',
        errorHTML: "",

        constructor: function(/*Object*/ args){
            declare.safeMixin(this, args);
        },
        
        animateErrors: function(){
            var node = dom.byId(this.errorContainer);
            var errHTML = this.errorHTML;
            node.innerHTML = errHTML;
            domClass.remove(node, 'display-none');
        }
    });
});

The Climax

What we have done here is that we have modularized our code and now we could simply require our form module and call the 'attachOnSubmitEvent' function and the rest would be taken care by the form module. Having said that, the content of our validateRegister.js and validateLogin.js would look something like:

//register form
require(["ready"], function(ready){
    ready(function(){
        require(["xvalidate", "my/form"], function(xvalidate, myForm){
            var profile = {
                trim: ["remail"],
                required: ["remail", "rpassword", "rrepassword"],
                constraints : {...},
                confirm: {...},
                invalid: {...},
                missing: {...}
            };
            // args : form id, profile and error container id
            myForm.attachOnSubmitEvent("register-form", profile, 'errors');
        });
    });
});
//login form
require(["ready"], function(ready){
    ready(function(){
        require(["xvalidate", "my/form"], function(xvalidate, myForm){
            var profile = {
                trim: ["lemail"],
                required: ["lemail", "lpassword"],
                constraints : {...},
                confirm: {...},
                invalid: {...},
                missing: {...}
            };
            // args : form id, profile and error container id
            myForm.attachOnSubmitEvent("login-form", profile, 'errors');
        });
    });
});

Summary:
1. Learned how to setup a package directory
2. Configured package for the loader
3. Created the form validation module and the error module
4. Learned how to add module aliases in the dojo config
5. Learned how to include ( or require ) a module

This certainly is a very big walk-through but it should be really useful for guys like me who just started to get along with Dojo 1.7. Please feel free to comment or provide suggestions
to improve this blog/tutorial.

The complete package file : my-package.zip

That's it folks...

Leave a Reply

You must be logged in to post a comment.