Dashboard > RIFE > ... > Tips and Tricks > Adding AJAX Validation To Your Forms
RIFE Log In | Sign Up   View a printable version of the current page.
Adding AJAX Validation To Your Forms


Added by Tyler Pitchford, last edited by Tyler Pitchford on Dec 28, 2007  (view change)
Labels: 
(None)

AJAX Validation

RIFE by default requires a round trip to the server for validation. This works wonderfully in RIFE, but errors aren't reported until the user attempts to submit the data. That's where the AJAX validation comes in. Instead of waiting for the user to submit the data, as soon as the user finishes typing on an element the data is validated by the server (using the constraints defined in the MetaData) and if there is an error it is returned back immediatly, no user interaction required.

The AJAX Modification has been designed to be 100% backwards compatible with existing RIFE applications and utilizes your existing constraints, validation, and error templating to validate the form fields live. The end result is all of your validation logic (client and server side) is centralized and handled by RIFE's constraints and validation engine. Therefore, if you add a new constraint to a bean, all of the forms using that Bean will automatically reflect the new changes; no more out of sync Front and Backends.

Because each field is validated individually, the framework only supports ERROR values of the format ERROR:property_name (ex: ERRORS:name, ERROR:date, etc.), previous version support MARK:property_name, but this was removed and is now used to provide better feedback to users (we'll look at this in a moment). To add automatic AJAX support, you only need to install AJAX Validation Framework into your application, add some additional data to your forms and then make one API call --that's it. In this article we're going to work through a quick example of RIFE's standard validation and then we'll discuss the modifications required to use the AJAX Validation Framework.

The AJAX Validation Framework supports the following features:

  • Support for Text, Textarea, Checkbox, Radio and Select fields (only type="file" is missing)
  • Multiple forms per page
  • Prefix support (use the optional <input type="hidden" name="prefix" value="<PREFIX HERE>"/>)
  • Automatic class="valid" / class="valid" class injection on validated elements
  • Proper postback handling (incase there is an error when the form is submitted and needs to be redisplayed to the user --ex. When a non-validated element is checked on Submit and is invalid --mainly type="file" issues
  • Automatic enabling / disabling of the submit button w/ proper enabling on postback
  • Only a single API api to enable the AJAX Validation

A standard RIFE validation setup

Below is the AjaxSample Bean:

package com.warfrog.beans;

public class AjaxSample {
	private int id = -1;
	private String name = null; //text
	private String password = null; //secret
	private String email = null; //select
	private int numbers = 0; //textarea (why not?)
	private String url = null; //radio
	private boolean agreedToTerms = false;
	private byte[] image = null; //file

        ...getters/setters removed for readability...
}

and the RIFE MetaData class adding constraints to the id and name fields of the AjaxSample Bean.

package com.warfrog.beans;

import com.uwyn.rife.cmf.MimeType;
import com.uwyn.rife.site.ConstrainedProperty;
import com.uwyn.rife.site.MetaData;

public class AjaxSampleMetaData extends MetaData {
	public void activateMetaData() {
		//not validated by ajax
		addConstraint(new ConstrainedProperty("id").identifier(true).unique(true));
		//validated: must be not Null and contain [a-Z]
		addConstraint(new ConstrainedProperty("name").notNull(true).regexp("[a-zA-Z]+"));
		//validated: must be not Null and 6 characters or more in length
		addConstraint(new ConstrainedProperty("password").notNull(true).minLength(6));
		//validated: must be not Null, No longer than 128, and contain an Email
		addConstraint(new ConstrainedProperty("email").notNull(true).maxLength(256).email(true));
		//validated: can be null, must be a number
		addConstraint(new ConstrainedProperty("numbers").regexp("[0-9]*"));
		//validated: must be not Null, No longer than 128, and contain an Email
		addConstraint(new ConstrainedProperty("url").notNull(true).maxLength(1024).url(true));
		//validated: must accept terms
		addConstraint(new ConstrainedProperty("agreedToTerms").notEqual(false));
		//not validated by ajax
		addConstraint(new ConstrainedProperty("image").mimeType(MimeType.IMAGE_JPEG).file(true).notNull(true));
	}
}

Here is the AjaxSampleElement, its functional but not pretty template and it's Element definition (for main.xml)

package com.warfrog.elements;

import com.uwyn.rife.engine.Element;
import com.uwyn.rife.site.Validated;
import com.uwyn.rife.template.Template;
import com.warfrog.beans.AjaxSample;
import com.warfrog.helpers.FormsHelper;

public class AjaxSampleElement extends Element {
	public void processElement() {
		Template template = getHtmlTemplate("ajaxsample");		
		AjaxSample sampleBean = (AjaxSample) getNamedSubmissionBean("ajaxsample_data", "ajaxsample");
		
		if(sampleBean != null && ((Validated)sampleBean).validate()){
			//do something with bean
		}else if(sampleBean != null){
			if(hasSubmission()){
				//generate the errors for the user
				generateForm(template, sampleBean);			
			}
		}	
		print(template);
	}
}
<r:i name="common.blueprint"/>

<r:bv name="content">	
	<r:b name="ERRORMESSAGE:*"> <span class="errormessage"><r:v name="ERRORMESSAGE"/></span> </r:b>	
	
	<r:b name="MANDATORY:name">Name required</r:b>
	<r:b name="INVALID:name">No special characters allowed</r:b>	
	
	<r:b name="MANDATORY:password">Must enter a password</r:b>
	<r:b name="WRONGLENGTH:password">The password must be at least 6 characters</r:b>	
	
	<r:b name="MANDATORY:email">Email Address required</r:b>
	<r:b name="WRONGLENGTH:email">Too long</r:b>	
	<r:b name="INVALID:email">You must select a valid email address</r:b>		
	
	<r:b name="NOTNUMERIC:numbers">You can only enter numbers in this box</r:b>	
	
	<r:b name="MANDATORY:url">Please select a valid url</r:b>
	<r:b name="WRONGLENGTH:url">Cannot be more than 1024 characters</r:b>	
	<r:b name="INVALID:url">The URL format is incorrect</r:b>					

	<r:b name="INVALID:agreedToTerms">You must accept the terms</r:b>
	
	<r:b name="MANDATORY:image">An image required</r:b>	
	
	<form name="ajaxsample_data" 
		action="<r:v name="SUBMISSION:FORM:ajaxsample_data"/>" 
		method="post" 
		enctype="multipart/form-data"
		class="cssForm"
	>		
		<r:v name="SUBMISSION:PARAMS:ajaxsample_data"/>			
				
		<!-- using RIFE's built in input generation -->		
		<label>Name:</label>
		<r:v name="FORM:INPUT:name"></r:v><r:v name="ERRORS:name"></r:v><br/>
		
		<label>Password:</label>
		<r:v name="FORM:SECRET:password"></r:v><r:v name="ERRORS:password"></r:v><br/>
		
		<label>Email:</label>
		<select name="email">
			<option value="">Select an email address</option>
			<option value="test@test.com">test@test.com</option>
			<option value="test2@test.com">test2@test.com</option>
			<option value="test3@test.com">test3@test.com</option>						
		</select><r:v name="ERRORS:email"></r:v><br/>
		
		<label>Type in a number:</label> 
		<r:v name="FORM:TEXTAREA:numbers"></r:v><r:v name="ERRORS:numbers"></r:v><br/>		
		
		<label>Pick a url:</label><br/>
		<input name="url" type="radio" value="">NOT A URL</input><r:v name="ERRORS:url"></r:v><br/>
		<input name="url" type="radio" value="http://www.warfrog.com">Warfrog</input><br/>
		<input name="url" type="radio" value="http://www.rifers.org">RIFE</input><br/>
		
		Select a file:
		<input type="file" name="image"/><r:v name="ERRORS:image"></r:v><br/>
		</br>
		<input name="agreedToTerms" type="checkbox" value="true">
                        Agree to Terms?</input><r:v name="ERRORS:agreedToTerms"></r:v><br/>		
		
		<input type="submit"/>
		
	<form>
</r:bv>
<element id="AjaxSample" 
		implementation="com.warfrog.elements.AjaxSampleElement" 
		url="/ajaxsample">
		<submission name="ajaxsample_data">
			<bean name="ajaxsample" classname="com.warfrog.beans.AjaxSample"/>
		</submission>
	</element>

The above code would generate a form for the AjaxSample Bean and validate the bean once the user hit the submit button. Before we modify the code above to use AJAX validation, we need to install the Validation Framework.

Installing the AJAX Validation Framework

The Validation Framework is relativly easy to install. First, download and un-rar the Ajax_Validation_Framework.rar (in the attachements section) to your RIFE application. The files can be places into a jar and used by RIFE if desired, but I've let them open for those who wish to modify the framework. Once you have all the files installed you'll simply need to modify your main.xml and add the Ajax Validation element to it:

<element id="VALIDATION" file="ajaxvalidation.xml" url="/validation"/>

Integrating AJAX Validation into your site

That's it. Now, let's modify the example form above to add AJAX Validation Support. First, the Framework needs to modify the template to handle the AJAX support. The modifcation to your element requires only 1 change. For example, the AjaxSampleElement above would be changed to this:

package com.warfrog.elements;

import com.uwyn.rife.engine.Element;
import com.uwyn.rife.site.Validated;
import com.uwyn.rife.template.Template;
import com.warfrog.beans.AjaxSample;
import com.warfrog.helpers.FormsHelper;

public class AjaxSampleElement extends Element {
	public void processElement() {
		Template template = getHtmlTemplate("ajaxsample");		
		AjaxSample sampleBean = (AjaxSample) getNamedSubmissionBean("ajaxsample_data", "ajaxsample");
		
		if(sampleBean != null && ((Validated)sampleBean).validate()){
			//do something with bean
		}else if(sampleBean != null){
			if(hasSubmission()){
				//generate the errors for the user
				generateForm(template, sampleBean);			
			}
		}
		FormsHelper.enableAjaxFormValidation(this, template); //NEW ADDITION	
		print(template);
	}
}

That's it for your Elements. Now, only the AjaxSample template needs to be modified. First, the template needs to include the /common/ajaxformvalidation template. Next, the form must include two hidden inputs named "bean" and "template". The bean field holds the full name of the bean that should be used to validate the data and the template is the template used to render the form. Finally, the AJAX Framework will only validate fields with a class of "validate", so every input you want validated must be of class validate. The framework automatically injects class types of "valid" and "invalid" into elements depending on if they validated correctly or not. Accordingly, if you include CSS styles for .valid and .invalid your form will auto respond visually to the user. It should be noted that upon post you should use MARK:property_name to inject invalid into the form elements to keep the look as similar as possible between posts. The modified AjaxSample template shown below demonstrates all of the information discussed above:

<r:i name="common.blueprint"/>
<r:i name="common.ajaxformvalidation"/> <!-- include the ajax validation script -->

<r:bv name="content">
	<!-- optionally define valid/invalid for pretty error formatting -->
	<style>
	<!--
	.invalid{ 
		border: 1px solid #B32400;
		background-color: #FFCCBF;
	}
	.valid{ 
		border: 1px solid #24B300;
		background-color: #CCFFBF;
	}
	-->
	</style>
	
	<!-- MARK:ERROR MUST be set to invalid for proper MARK injection -->
	<r:b name="MARK:ERROR">invalid</r:b>		
	<r:b name="ERRORMESSAGE:*"> <span class="errormessage"><r:v name="ERRORMESSAGE"/></span> </r:b>	
	
	<r:b name="MANDATORY:name">Name required</r:b>
	<r:b name="INVALID:name">No special characters allowed</r:b>	
	
	<r:b name="MANDATORY:password">Must enter a password</r:b>
	<r:b name="WRONGLENGTH:password">The password must be at least 6 characters</r:b>	
	
	<r:b name="MANDATORY:email">Email Address required</r:b>
	<r:b name="WRONGLENGTH:email">Too long</r:b>	
	<r:b name="INVALID:email">You must select a valid email address</r:b>		
	
	<r:b name="NOTNUMERIC:numbers">You can only enter numbers in this box</r:b>	
	
	<r:b name="MANDATORY:url">Please select a valid url</r:b>
	<r:b name="WRONGLENGTH:url">Cannot be more than 1024 characters</r:b>	
	<r:b name="INVALID:url">The URL format is incorrect</r:b>					

	<r:b name="INVALID:agreedToTerms">You must accept the terms</r:b>
	
	<r:b name="MANDATORY:image">An image required</r:b>	
	
	<form name="ajaxsample_data" 
		action="<r:v name="SUBMISSION:FORM:ajaxsample_data"/>" 
		method="post" 
		enctype="multipart/form-data"
		class="cssForm"
	>		
		<r:v name="SUBMISSION:PARAMS:ajaxsample_data"/>	
		<input type="hidden" name="bean" value="com.warfrog.beans.AjaxSample"/>	
		<input type="hidden" name="template" value="ajaxsample"/>			
		<!--OPTIONAL PREFIX TAG [NOT USED IN THIS EXAMPLE] <input type="hidden" name="prefix" value="ajax_"/>-->
				
		<!-- using RIFE's built in input generation -->		
		<label>Name:</label>
		<r:v name="FORM:INPUT:name"></r:v><r:v name="ERRORS:name"></r:v><br/>
		<!-- must use a BV for MARK injection, because nested values are not supported -->
		<r:bv name="FORM:INPUT:name">class="validate <r:v name="MARK:name"></r:v>"</r:bv>
		
		<label>Password:</label>
		<r:v name="FORM:SECRET:password"></r:v><r:v name="ERRORS:password"></r:v><br/>
		<r:bv name="FORM:SECRET:password">class="validate <r:v name="MARK:password"></r:v>"</r:bv>		
		
		<label>Email:</label>
		<select name="email" class="validate <r:v name="MARK:email"></r:v>">
			<option value="">Select an email address</option>
			<option value="test@test.com">test@test.com</option>
			<option value="test2@test.com">test2@test.com</option>
			<option value="test3@test.com">test3@test.com</option>						
		</select><r:v name="ERRORS:email"></r:v><br/>
		
		<label>Type in a number:</label> 
		<r:v name="FORM:TEXTAREA:numbers"></r:v><r:v name="ERRORS:numbers"></r:v><br/>
		<r:bv name="FORM:TEXTAREA:numbers">class="validate <r:v name="MARK:numbers"></r:v>"</r:bv>
		
		<label>Pick a url:</label><br/>
		<input name="url" type="radio" class="validate<r:v name="MARK:name"></r:v>" value="">
                        NOT A URL</input><r:v name="ERRORS:url"></r:v><br/>
		<input name="url" type="radio" class="validate<r:v name="MARK:name"></r:v>" value="http://www.warfrog.com">
                        Warfrog</input><br/>
		<input name="url" type="radio" class="validate<r:v name="MARK:name"></r:v>" value="http://www.rifers.org">
                        RIFE</input><br/>
		
		Select a file:
		<input type="file" name="image" class="<r:v name="MARK:image"></r:v>"/><r:v name="ERRORS:image"></r:v><br/>
		</br>
		<input name="agreedToTerms" type="checkbox" class="validate<r:v name="MARK:name"></r:v>" value="true">
                        Agree to Terms?</input><r:v name="ERRORS:agreedToTerms"></r:v><br/>		
		
		<input type="submit"/>
		
	<form>
</r:bv>

That's it, the AJAX Validation framework will handle everything else. At this point, every part of the AjaxSample bean is validated before the post except the file field and if the user leaves the file field blank RIFE's built in postback validation handles everything correctly at that point.



Are you enjoying Confluence? Please consider purchasing it today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.2.1a Build:#515 May 19, 2006) - Bug/feature request - Contact Administrators