[Image] EchoPoint
Helping you build truly dynamic and stateful web applications!
nothing

EchoPoint Validation

By Brad Baker
Friday, May 21, 2004

EchoPoint now has validation support. 

All serious applicatioins require some sort of data validation and you can use the new echopoint.valdation.* package to do it. 

Validation can occur on the client side, the server side and preferrably both.  There are a number of validation related classes available to define how the validation will operate, as well a some simple validation implementation classes and a starter library of client side JavaScript rules.

But first a word on validation and validation libraries.  While the idea of validation is generic and is required in virtually all applications, developing a generic validation API is inherently difficult.  This is because validation involves not only finding out if a field is invalid but what do you with it afterwards; and how do you store valid fields plus a myriad of other coding questions.

Other issues are when do you run validation?  Should you run it on the client or on the server.  Should it run when the user leaves a field or only if it has changed? Should you run all the rules in a set or stop if one of them fails? 

These design questions have led the EchoPoint validation to have the following aims.  You can be the judge of whether it acheives them :

  • Be easy to use to get simple validation up and running
  • Allow client side and server side validation
  • Allow for complex validation schemes to be developed
  • Be extensible by developers
  • Allow for configurable run time options
  • Provide a library of useful client side validation functions

The echopoint.validation package - interfaces provided

echopoint.validation.Validateable

The top level interface in the validation library is echopoint.validation.Validateable.  This is something that can be validated (and yes I know Validateable is not an adjective but its close).  A Validateable has the following methods:

  • Locale getLocale()
    • returns a Locale to help with internationalisation
  • String getText()
    • returns the text to be validated
  • Object getIdentifier()
    • returns an identifier to allow Validateable to be named
  • ValidationRuleSet getRuleSet()
    • The ValidationRuleSet contains ValidationRules

If the methods above look familair, then they are.  The first three are also in the nextapp.echo.TextField component.  However this component does not implement Validateable (how could it?).  Rather there is now an echopoint.TextField that derives from nextapp.echo.TextField, and it implements Validateable.  It is this new text field class that can be validated.

The final method getRuleSet() returns a ValidationRuleSet which is explained next.

echopoint.validation.ValidationRuleSet

This interface is used to contain an aray of rules, options on how to run them and what the name of the field is.  It has the following methods :

  • String getName(Validateable)
    • Returns the name of the Validateable.  This can be used in error messages and field references.  More in this later.
  • int getRunOptions()
    • Returns an integer OR'ed with the constants RULE_ON_BLUR_RUN_ALL | RULE_ON_SUBMIT_RUN_ALL | RULE_ON_BLUR_RUN_AGGRESSIVE
  • ValidationRule[] getRules()
    • An array of ValidationRules to validate

The getName() method can be used to unqiue identify a Validateable.  This will probably be a unique but human readable name like 'suburb' or 'income' or 'postcode'.  While Validateable does have a getIdentifier() method, often this needs to be used for other purposes, as well as valdaition and hence the getName() method is invoked to allow a displayable name to be synthesized.

The runOptions are an OR'ed together integer flag field. 

  • If RULE_ON_BLUR_RUN_ALL is present, then the validation code will run all the rules in the rule set during the field blur, even if one fails.  This of course may result in multiple error messages being displayed to the user.  If this flag value is not present, then the validation will stop after the first rule that fails.
  • If RULE_ON_SUBMIT_RUN_ALL is present, then the validation code will run all the rules in the rule set during the server submission, even if one fails.  This of course may result in multiple error messages being displayed to the user.  If this flag value is not present, then the validation will stop after the first rule that fails.
  • If RULE_ON_BLUR_RUN_AGGRESSIVE is present, then the rules will be evaluated each time user leaves a field.  This can be quite annoying, especially if the user is trying to navigate to another field to correct it.  The default behaviour is for the rules to evaluated when ever the fiel has changed in value.

Finally the getRules() method returns and array of ValidationRule objects that are the rules to be evaluated to determine if a field is Validateable.

echopoint.validation.ValidationRule

This interface is used to contain a validation rule.  It can be a client side JavaScipt rule or a server side rule.

  • String getErrorMessage(Validateable validateable, String fieldName)
    • Returns an error message to be displayed if the rule is invalid.
  • String getJSErrorMessage(Validateable validateable, String fieldName)
    • Returns an error message
  • String getJS(Validateable validateable, int runOption)
    • Returns a JavaScript expression to be evaluated on the client side
  • boolean isValid(Validateable validateable)
    • Returns true if the Validateable provide is valid in the content of this rule

The getErrorMessage() method is self explanatory.  It returns an error message that may be displayed if the rule evaluated to false.  The Validateable is provide as well as the fieldName that as returned from ValidationRuleSet.getName().  This is intended to allow you to provide a custom error message that optionally includes the field name.  The Validateable has a Locale object and hence you can use it to localise any error message returned.

The getJSErrorMessage() method is a special version of getErrorMessage() that allows you to return a different error message to be presented to the user at the client versus what is generated at the server.  A default implementation might just do a direct call to getErrorMessage().

The getJS() method is invoked to return a JavaScript expression, that is to be used on the client side to determine if a field is valid.  If the expression evauluates to true, then the field is considered valid. 

When validation is performed on the client side, it can run when the user leaves a field (which I have called field blur) and when the application is about to submit something back to the server (which I have called server submission).  The runOption variable will be either RUN_ON_BLUR or RUN_ON_SUBMIT.  This method will be invoked twice to provide a rule that runs on field blur and one that runs on server submission.  If it return null, then no rule is evaluated and hence is assumed to be valid.

There are two "magic variables" made available to the JavaScript expressions.  The first is called "value" and its the current string value of the field to be validated.  This is the variable you will use most of the time.  The second magic variable is called "element" and this is the HTML element that is being evaluated.  More on this later.

The isValid() method is for server side validation of a Validateable.  This method returns true of a Validateable is considered valid.

echopoint.validation.ValidationSubmitter

This interface is used to indicate that an object will cause client side validation to occur.

  • boolean isValidationEnabled()
    • If this returns false, then no validation should occur.
  • int getRunOptions()
    • If this returns RULESETS_RUN_ALL, then all the rulesets present should be evaluated, even if one of them fails.

The isValidationEnabled() method allows you to turn client side submit validation on or off.  The getRunOptions can return RULESETS_RUN_ALL, in whcih case all the rule sets present will be evaluated.  This could result in many error messages being displayed.  The default behaviour is for the validation to stop at the first rule set that fails validation.

The echopoint.PushButton now implements ValidationSubmitter and hence can be used to control submitting validateable data. 

This use of objects that implement this ValidationSubmitter interface allows you to have some buttons on the screen that cause validation (eg. an OK button) while having others that do no cause validation (eg. a Cancel button).  And this behaviour can be dynamically turned on and off via isValidationEnabled() and its counterpart method PushButton.setValidationEnabled().

 

The echopoint.validation package - SimpleRuleSet and SimpleRule

There are two classes provide in the package which are very simple implementations of the interfaces. 

The first is SimpleRuleSet, which allows for rules to be dynamic added and removed from a rule set.  The second is SimpleRule that takes an error message and JavaScript expression  and returns them as it implements ValidationRule

This simple classes can form the basis of new rule sets and validation rules or you can forgo them and created your own classes based directly on the valdation interfaces.  I have shied away from providing a raft of ValidationRule implementations because it is very difficult to come up with generic classes that can be used in all cases.

echopoint.TextField

The new class echopoint.TextField is derived from nextapp.echo.TextField and adds, amongst other things, the ability to perform validation on its contents.  You can do this because it now implements Validateable.

Note the new getRuleSet() and setRuleSet() methods.  These allow a ValidationRuleSet to be associated with the TextField.  This in turn contains rules and hence validation can be done.  If the validation support code cannot find any client side validation expressions, then nothing is performed and the field is considered valid.

Here is some silly example code for an imaginary text field called "age"

SimpleRuleSet ruleSet = new SimpleRuleSet();

SimpleRule rule =
new SimpleRule("isInteger(value) && parseInt(value) > 18", "You must be over 18!");
ruleSet.add(rule);

rule = new SimpleRule("fieldIsValid('income')", "The income field must be valid as well");
ruleSet.add(rule);

rule =
new SimpleRule("fieldHasValue('postcode')", "The postcode field must have a value as well");
ruleSet.add(rule);

TextField tf =
new TextField();
tf.setRuleSet(ruleSet);
IdentifierKit.set(tf,
"validationName","age");

The code above "names" the TextField "age" for the purposes of validation.  It has three client side JavaScript expressions which must all evaluate to true in order for the field to be considered valid. 

Note the use of boolean shortcut logic.  If isInteger() expression returns false, then the parseInt() call will never be made.  You will probably use a lot of this type of logic in your JavaScript rules.  However the JavaScript eval() method is incrediable powerful and you can write quite lengthy rules, as long as they evaluate to true or false.  For example you can write a rule with comlete statements in it like  :

if (isInteger(value)) {
     if ( parseInt(value) > 18) {
           return true;
     }
}
return false;

You can even write define functions inside the JavaScript expression and refer to them later.  For example :

function myEvenNumber(value) {
      if ( parseInt(value) % 2 == 0)
            return true;
      else
           return false;
}

isInteger(value) && myEvenNumber(value);

This works because the final part of the expression evaluates to true or false.  If you are concerned about your custom functions being repeated throughout the page, you could used a JavaScriptInclude component to include the common routines and then refer to them in your client validation rules.

There is a number of pre-built JavaScript funcitons made available in EchoPoint.  These are listed in detail in the ValidationRule javadoc.   But in short they are :

  • isInteger(value)
  • isIntegerStrict(value)
  • isSignedInteger(value)
  • isSignedIntegerStrict(value)
  • isDecimal(value)
  • isDecimalStrict(value)
  • isSignedDecimal(value)
  • isSignedDecimalStrict(value)
  • isCurrency(value)
  • isCurrencyStrict(value)
  • isSignedCurrency(value)
  • isSignedCurrencyStrict(value)
  • insideRange(value,min,max)
  • outsideRange(value,min,max)
  • isDate(value)
  • isDateStrict(value)
  • dateIsValid(value, preferEuroFormats)
  • dateIsValidStrict(value, preferEuroFormats)
  • dateIsAfter(dateValue1, dateformat1, dateValue2, dateformat2)
  • fieldIsValid(fieldName)
  • fieldIsNull(fieldName)
  • fieldIsEmpty(fieldName)
  • fieldHasValue(fieldName)
  • fieldGetValue(fieldName)

 

Now the SimpleRule always returns true during the server side validation isValid() method.  Server side validation is a very problem domain specific area and SimpleRule has chosen to avoid it all together.  But you could do something like this :


    rule = new SimpleRule("isInteger(value) && parseInt(value) > 18", "You must be over 18!") {
          public boolean isValid(Validateable validateable) {
               
    // do some problem domain specific code here
               
    return mySpecificValidationRoutine(validateable);
          }
    };

    echopoint.validation.ValidationKit

    There is a class called echopoint.validation.ValidationKit that provides helper methods for server side validation.  The validate(Component c, ValidationCallback validationCallback) method will traverse a Component hierarchy  and find all components that implement Validateable and then validate them.  It uses the echopoint.validation.ValidationCallback interface to call back to some code when a rule fails, when a rule set fails or when the Validateable is found to be valid. 

    For example here is some code that doesn't do any particular but it gives you the general code structure :

    ValidationKit.validate(topLevelComponent, new ValidationCallback() {
          public void onValid(Validateable validateable) {
                // do somethig with the valid Validateable
               
    // like store its text some where, maybe a database
         
    }

          public boolean onInvalid(Validateable validateable) {
               
    // do somethig with the invalid Validateable
               
    // like display some error message
               
    return true;
          }

          public
    boolean onInvalidRule(Validateable validateable, ValidationRule rule) {
               
    // optionaly do something on an failed rule
               
    // like display some error message
               
    return true;
          }
    });

    Exactly what you do in the call back is up to you.   Again the framework has deliberately chosen not to do anything on the server side since it is such a problem domain specific issue.

     


    Home

    SourceForge Logo

    The EchoPoint project is kindly hosted by SourceForge