Displaying Error Messages
WebWork stores validation error messages in two different places,
depending on the error. Errors that were added using
addActionError() can be retrieved with
getActionErrors(), while errors that were added using
addFieldError() can be retrieved with
getFieldErrors(). When validating fields using XML
files, WebWork uses addFieldError() to populate the
errors.
When displaying messages, we must check both cases. The method
getActionErrors() returns a List of
Strings for us to easily loop over. However,
getFieldErrors() returns a Map of
Lists where the keys of the map correspond to the
fields and the values correspond to the error messages associated
with the field. In order to display all field-level messages, a
nested loop is required.
<ww:if test="hasErrors()">
<p style="color: red;">
<b>Errors:</b>
<ul>
<ww:if test="hasActionErrors()">
<ww:iterator value="actionErrors">
<li style="color: red;"><ww:property/></li>
</ww:iterator>
</ww:if>
<ww:if test="hasFieldErrors()">
<ww:iterator value="fieldErrors">
<ww:iterator value="value">
<li style="color: red;"><ww:property/></li>
</ww:iterator>
</ww:iterator>
</ww:if>
</ul>
</p>
</ww:if>
The benefit of adding field-level errors is that it associates
errors with an input field, allowing errors to be displayed next to
the input field. Using the default theme attribute in
the input type displays error messages next to the field. However,
in the earlier example, I've set the theme attribute
to simple, which does not do this. The disadvantage of
using field-specific errors is that if you want to display the
messages in a nested for loop (like above), they will not come in
order, since a Map's keys are being iterated over.
Action-level errors are stored in a List and will
print in the order in which they were added.
Custom Validators
When none of WebWork's built-in validators fit your business
needs, you can quite easily declare a custom validator and have it
behave the way you want. It can be invoked and used just like the
built-in validators.
To create a custom validator, you must define a class that
implements the Validator interface, which contains
several methods. Fortunately for us, in most cases we don't need to
implement all of the methods and instead can extend the
ValidatorSupport class, which is an abstract class
implementing the Validator interface. All we are left
to do is implement the validate(Object) method.
Example 5 uses a custom validator to force the user to enter a
Canadian postal code if Canada is selected as the country, and an
American one if America is the chosen country.
Let's have a look at the validate(Object) method to
see how this can be done.
public void validate(Object object)
throws ValidationException {
String country = (String)
getFieldValue("country", object);
String zipCode = (String)
getFieldValue("zipCode", object);
ValidatorContext ctxt = getValidatorContext();
if ("CAN".equals(country)) {
if (!Pattern.matches(
"[a-zA-Z]\\d[a-zA-Z]\\d[a-zA-Z]\\d",
zipCode)) {
ctxt.addActionError(
"Invalid Canadian Zip code (Eg: A1B2C3)");
return;
}
} else if ("US".equals(country)) {
if (!Pattern.matches("\\d{5}", zipCode)) {
ctxt.addActionError(
"Invalid US Zip Code (Eg: 64145)");
return;
}
}
}
To use your custom validator, you must first register it by
adding an entry for it in the validators.xml file.
<validator name="zipcode"
class="com.esolaria.webworkapp.ex5.ZipCodeValidator"/>
The alias given to the validator is zipcode, which is what
we will refer to it by in UserSignupAction-validation.xml
file, as shown below:
<field name="country">
<field-validator type="requiredstring">
<message>Please select a country</message>
</field-validator>
</field>
<validator type="zipcode">
<message/>
</validator>
Client-Side Validation
WebWork provides client-side validation via the
DWRValidator class. It uses Direct Web Remoting to validate
fields on onblur events. To use client-side
validation, the <ww:form> should read:
<ww:form action="submitForm" name="submitForm"
validate="true">
<ww:text name="name" required="true"/>
<ww:submit name="btn" value="Submit" />
</ww:form>
Lack of proper client-side validation is one of the shortcomings of
WebWork and will undoubtedly be addressed when it is
merged with Struts. Using Direct Web Remoting in this manner
has performance and load issues that can be avoided by using simple
JavaScript validation. If you want client-side validation, the
Jakarta
Commons Validator is a good option.
Conclusion
You should now have a good understanding of how to validate web
forms using WebWork's validation framework. The authoritative text
on WebWork is WebWork in
Action, written by its creators Patrick Lightbody and Jason
Carreira. It is a valuable resource for anyone working with WebWork
and also covers validation in greater detail.
Resources