Friday, September 12, 2008

CRM Form Validator

A few months back, I did a tech presentation to one of my client’s development team. As we where going over customization, talking about all the amazing features dynamics has to offer out of the box, the pm realized that the platform does not provide a solid validation mechanism that would support their complicated requirements. He was right, obviously, and seemed bothered about it. Of course the dev guys told that this is not a big deal.

Never the less, I already had a solid answer that was part of my infrastructure and so just for the sake of discussion I asked him whether he would you be satisfied with a solution that requires him to write a single line of code per validation request. His answer was obvious, but he wanted proof and so I drew these few lines on his white board:


var frmValidator = new FormValidator(true);
frmValidator.AddRule( "accountnumber" , "^.{10}$" , "Account Number must contain 10 letters" );



Luckily, the pm had development skills and so he immediately understood what I wrote.
The CTO mentioned that “most” fields can’t rely on just a single regular expression validation.
I can’t argue with that, I said, and drew the following punch lines:


//Multiple Validation per field
frmValidator.AddRule( "accountnumber" , "^.{10}$" , "Account Number must contain 10 letters" );
frmValidator.AddRule( "accountnumber" , "[a-z]{4}-[0-9]{2}-[A-Z]{2}" , "Account Number Format should be 'abcd-12-EF'" );
//OR
//Ability to write custom validation functions
frmValidator.AddRule( "name" , KeepEveryoneHappy , "I am not happy" );

function KeepEveryoneHappy()
{
return typeof("and they lived happily ever after") == "string"
}


Did I here a laugh, bad coding joke.

This piece of code is a crucial part of my infrastructure template. I never start coding client side tasks from scratch. You should consider building your own JavaScript template that encompasses all common development tasks. The grand idea is to put all your effort and time (is money) in building your customer’s business logic and save as much time when it comes to technology. That is what a dynamic(s) platform is about.

The FormValidator can either present a single validation message and focus the underline field of summarize all validations into one alert message. This is set by passing the true / false flag to the FormValidator constructor.


var frmValidator = new FormValidator(true);


The FormValidator has a simple hash ( dictionary ) table which enables you to write multiple validation for a single field. So if you can’t or don’t master regex you can accomplish that task by providing a number of simple validadtion expressions instead on just one.


frmValidator.AddRule( "accountnumber" , "^.{10}$" , "Account Number must contain 10 letters" );
frmValidator.AddRule( "accountnumber" , "[a-z]{4}-[0-9]{2}-[A-Z]{2}" , "Account Number Format should be
'abcd-12-EF'" );


If the validation requires a more complex solution or you need to make extra checks that can’t be achieved via regex you can specify a function pointer that makes the check. The function return value must be Boolean.


frmValidator.AddRule( "name" , KeepEveryoneHappy , "I am not happy" );

function KeepEveryoneHappy()
{
return typeof("and they lived happily ever after") == "string"
}


The AddRule method receives the name of the field , a regex or a function pointer and the error message to display when things go wrong.

I hope you find this a useful.
Here is the entire example:


function OnCrmPageLoad()
{
var frmValidator = new FormValidator(true);
//Multiple Validation per field
frmValidator.AddRule( "accountnumber" , "^.{10}$" , "Account Number must contain 10 letters" );
frmValidator.AddRule( "accountnumber" , "[a-z]{4}-[0-9]{2}-[A-Z]{2}" , "Account Number Format should be 'abcd-12-EF'" );

//Ablity to write cusotm validation functions
frmValidator.AddRule( "name" , KeepEveryoneHappy , "Name must be longer then 2 letters" );
}

function KeepEveryoneHappy()
{
return true; //and they lived happily ever after
}



function FormValidator(mode)
{
var Instance = this;

Instance.Mode = mode;
Instance.Keys = [];
Instance.Values = [];
Instance.Summery= "";

Instance.AddRule = function( controlId , validation , errorMessage )
{
if( isNullOrEmpty(Instance.Values[controlId]) )
{
Instance.Keys[Instance.Keys.length] = controlId;
Instance.Values[controlId] = [];
}
var iControlValidations = Instance.Values[controlId].length;
var control = document.getElementById( controlId );
if( isNullOrEmpty( control ) )
return alert( "The Field: " + controlId + " is undefined" );
var rule = new ValidationRule( control , validation , errorMessage );
Instance.Values[controlId][iControlValidations] = rule;
}

function ValidationRule( control , validation , errorMessage )
{
this.Control = control;
this.Validation = validation;
this.Message = errorMessage;
}

function OnCrmPageSave()
{
var valid4Submit = true;
for( var i = 0 ; i < Instance.Keys.length ; i++ )
{
var controlValidations = Instance.Values[Instance.Keys[i]]
for( var j = 0 ; j < controlValidations.length ; j++ )
{
var rule = controlValidations[j];
switch( typeof(rule.Validation) )
{
case "string":
var DataValue = rule.Control.DataValue;
if( DataValue != null )
valid4Submit = rule.Control.DataValue.match( rule.Validation ) != null;
else
valid4Submit = true;
break;
case "function":
valid4Submit = rule.Validation()
break;
}

if( !Instance.Mode && !valid4Submit )
{
alert( rule.Message );
rule.Control.focus();
return ( event.returnValue = valid4Submit );
}
else if( !valid4Submit )
{
Instance.Summery += rule.Message + "\n";
}
}
}

ShowSummery();
return ( event.returnValue = valid4Submit );
}

function ShowSummery()
{
if( !isNullOrEmpty(Instance.Summery) )
{
alert( Instance.Summery );
Instance.Summery = "";
}
}

function Init(){
crmForm.attachEvent( "onsave" , OnCrmPageSave );
}

function isNullOrEmpty( obj ){
return obj == null || typeof(obj) == "undefined" || obj == "";
}

Init();
}

OnCrmPageLoad();

3 comments:

Anonymous said...

You just saved me a lot of trouble trying to come up with a solid validation framework. Thanks!

Anonymous said...

Great! How do I set focus on the invalid field once they click on ok?
Thanks!

Saritha said...

Works great! Thanks for posting.