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; private String password = null; private String email = null; private int numbers = 0; private String url = null; private boolean agreedToTerms = false;
private byte[] image = null;
...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() {
addConstraint(new ConstrainedProperty("id").identifier(true).unique(true));
addConstraint(new ConstrainedProperty("name").notNull(true).regexp("[a-zA-Z]+"));
addConstraint(new ConstrainedProperty("password").notNull(true).minLength(6));
addConstraint(new ConstrainedProperty("email").notNull(true).maxLength(256).email(true));
addConstraint(new ConstrainedProperty("numbers").regexp("[0-9]*"));
addConstraint(new ConstrainedProperty("url").notNull(true).maxLength(1024).url(true));
addConstraint(new ConstrainedProperty("agreedToTerms").notEqual(false));
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()){
}else if(sampleBean != null){
if(hasSubmission()){
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"/>
<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()){
}else if(sampleBean != null){
if(hasSubmission()){
generateForm(template, sampleBean);
}
}
FormsHelper.enableAjaxFormValidation(this, template); 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"/>
<r:bv name="content">
<style>
<!--
.invalid{
border: 1px solid #B32400;
background-color: #FFCCBF;
}
.valid{
border: 1px solid #24B300;
background-color: #CCFFBF;
}
-->
</style>
<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"/>
-->
<label>Name:</label>
<r:v name="FORM:INPUT:name"></r:v><r:v name="ERRORS:name"></r:v><br/>
<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.