Form Validation With Custom Modules In Dojo 1.7
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...
Filed under: Javascript



