Behavior: Validation and Dependenices

posted Jan 24, 2012, 9:12 AM by Eric Patrick   [ updated Feb 15, 2013, 6:36 AM ]

Background

The qbo.Validation.js plugin includes a 'Validator' behavior which extends the MooTool's Form.Validation functionality, including:
  • Data type validation: dates, integers, money, phones, email addresses, and more,
  • Required field enforcement
  • Dependency checking
    • e.g. enable Field C if Field A or Field B are filled in
    • e.g. enable Field C if Field A and Field B are filled in
    • e.g. hide a section of a form unless Checkbox D is checked
The examples below have been implemented in source > qbo3 > qbo.ApplicationWeb > Templates > Samples > Form.Validation.xslt. Note that all examples assume we are coding XSLT, so JSON braces are doubled (e.g. {{ }} instead of {}).

Adding the Behavior

To leverage this behavior, you should add a Validate behavior to some tag surrounding your form elements, such as a div or form tag. For QBO 3, the pattern to use is form and fieldset tags, but the behavior does not require this.

<form id="MyForm" data-behavior="Validator">
  <fieldset>
    <input type="text" name="Email" class="qbo-email" value="{//Email}"/>
  </fieldset>
</form>

The Validator javascript classes include a series of css class names that are matched to validation code. For example:
  • 'qbo-money': this css class will cause validation that the input is currency
  • 'qbo-date': this css class will  cause validation that the input is a valid date
  • 'required': this css class will cause validation that the input is not empty
Validators can be combined. For example, a required money field would be:

<input class="required qbo-money" type="text" name="MyAmount" value="{// MyAmount }"/>

When a field is validated, the following happens:
  • if valid, the css class 'validation-passed' is added to the element, and an 'elementPass' event is raised
  • if invalid, the css class 'validation-failed' is added to the element, and an 'elementFail' event is raised
  • if extended validation classes are used (e.g. Form.Validator.Inline or Form.Validator.Tips), a message is displayed to the user
In QBO 3, the standard style sheets include:
  • required: adds a red bottom border to the element
  • validation-passed: adds a green bottom border to the element
  • validation-failed: adds a red background color to the element
To check to see if all fields pass validation, one can call the validate method on the Form.Validator javascript class like this:

document.id('MyForm').retrieve('validator').validate();

This method will cycle through every element with a validation class, and call the validation code behind it. Thus, a user would see an invalid fields suddenly appear with a red background color (because the validation-failed class will be added to them).

Virtually every part of form validation can be overridden by extending existing classes or hooking into existing validation events.

Adding Dependencies

A 'dependency' includes the following use cases:
  • Field A should be enabled only if Field B is not empty
  • Field A should be enabled only if Field B, C, ... or N is not empty
  • Field A should be enabled only if Fields B, C, ... and N are not empty
  • An element should be visible only if Field B is not empty
  • An element should be visible only if Field B, C, ... or N is not empty
  • An element should be visible only if Fields B, C, ... and N are not empty
Note that the Depend behavior works well with the 'required' validation; if elements are disabled, they are not considered required.

Use Case 1: Field A should be enabled only if Field B is not empty

<input type="text" id="FieldA" name="FieldA" value="{//FieldA}" 
  data-behavior="Depend" data-depend-options="{{'depends': 'FieldB'}}"/>
<input type="text" id="FieldB" name="FieldB" value="{//FieldB}"/>

Use Case 2: Field A should be enabled only if Field B or C is not empty

<input type="text" id="FieldA" name="FieldA" value="{//FieldA}" 
  data-behavior="Depend" data-depend-options="{{'depends': 'FieldB,FieldC'}}"/>
<input type="text" id="FieldB" name="FieldB" value="{//FieldB}"/>
<input type="text" id="FieldC" name="FieldC" value="{//FieldC}"/>

Use Case 3: Field A should be enabled only if Fields B and C are not empty

<input type="text" id="FieldA" name="FieldA" value="{//FieldA}" 
  data-behavior="Depend" data-depend-options="{{'depends': 'FieldB,FieldC', 'condition': 'and'}}"/>
<input type="text" id="FieldB" name="FieldB" value="{//FieldB}"/>
<input type="text" id="FieldC" name="FieldC" value="{//FieldC}"/>

Use Case 4: Show a group of elements only if a checkbox is checked

<input type="checkbox" id="ExtraStuff" value="1"/>
<div data-behavior="Depend" data-depend-options="{{'depends': 'ExtraStuff'}}">
  <input type="text" name="FirstName" value="//FirstName"/>
  <input type="text" name="LastName" value="//LastName" class="required"/>
</div>

Options for the Depend behavior:
  • depends (required): a list of element names that determine whether an element is enabled or not
  • condition (option, default is 'or'): 'and' or 'or'
  • resetOnDisable (optional, default is true): if true, disabled fields will have their value reset to the original values at rendering time when disabled
  • emptyOnDisable (optional, default is false) : if true, disabled fields will have their value removed when disabled
  • required (optional, default is true) : if true, the required validator is removed for disabled fields
Example using all options:

<input type="text" id="FieldA" name="FieldA" value="{//FieldA}"
  data-behavior="Depend" data-depend-options="{{
    'depends': 'FieldB,FieldC', 
    'condition': 'and',
    'resetOnDisable': 'false',
    'emptyOnDisable': 'true',
    'required': 'true'
}}"/>

Requiring At Least One Element

Use cases include requiring at least one of multiple elements to be selected, such as:
  • at least one radio button, or 
  • one or more checkboxes
To do this, wrap a group of elements with a <fieldset class="required"> tag. The qbo.Valuation.js extends the Form.Validator classes to support required fieldsets. If any element within the fieldset has a value, the required condition is met.

Radio button example:

<fieldset class="required" id="ChooseOne">
<input type="radio" id="ChooseOne1" name="ChooseOne" value="1"/>
<label class="radio" for="ChooseOne1">One</label>
<br/>
<input type="radio" id="ChooseOne2" name="ChooseOne" value="2"/>
<label class="radio" for="ChooseOne2">Two</label>
</fieldset>

Checkbox example:

<fieldset class="required" id="ChooseMany">
<input type="checkbox" id="Many1" name="Many1" value="1"/>
<label class="radio" for="Many1">One</label>
<br/>
<input type="checkbox" id="Many2" name="Many2" value="3"/>
<label class="radio" for="Many2">Two</label>
<br/>
<input type="checkbox" id="Many3" name="Many3" value="3"/>
<label class="radio" for="Many3">Three</label>
</fieldset>

Form Submission

Nearly every QBO 3 form has data submitted by AJAX, instead of an actual form submission to the server. Such AJAX calls are typically invoke by clicking on a button.

However, if a user simply clicks enter in one of the fields, the browser will submit the form.  This is generally bad, since it avoids data validation, and the form usually has no action, so it just throws the user's data away.

The Validator behavior by default prevents this by issuing form.addEvent('submit', function() { return false; })

If a form using the Validator control must actual submit via a 'regular' form post, use the following:

<form action="..." data-behavior="Validator" data-validator-options={{ 'preventSubmit': false}}>


Comments