Tuesday, December 21, 2010

JSF2.0 component for cross-field validation


Have you ever had problems with cross-field validation in JSF? Me too, so I created this component. You can validate few UIInput components and have their values as List in validator. The component is in softwaremill-faces library. To use it in maven project, add repository:

<repository>
  <url>http://tools.softwaremill.pl/nexus/content/groups/smlcommon-repos/ </url>
  <layout>default</layout>
  <releases>
    <enabled>true</enabled>
  </releases>
  <snapshots>
    <enabled>true</enabled>
  </snapshots>
</repository>
and dependency:
<dependency>
  <groupId>pl.softwaremill.common</groupId>
  <artifactId>softwaremill-faces</artifactId>
  <version>43-SNAPSHOT</version>
</dependency>
Now you can use multiValidator component. First add namespace to your .xhtml page:
xmlns:v="http://pl.softwaremill.common.faces/components"

Then just wrap components you want to cross-validate in <v:multiValidator>. If you attach validator to this component, value parameter that goes to validation method is List of values of UIInput components inside  tag.

E.g. if you want to validate two checkboxes. Each can be checked or unchecked, but at least one has to be checked.
<v:multiValidator id="multi" validator="#{bean.validationMethod}">
  <h:selectBooleanCheckbox value="#{bean.check1}" />
  <h:selectBooleanCheckbox value="#{bean.check2}" />
</v:multiValidator>
<h:message for="multi" />
Validation method in bean:
public void validationMethod(FacesContext context, UIComponent component, Object value) {
  List<Object> values = (List<Object>) value;
  //value is list of values of both selectBooleanCheckboxes
  Boolean firstChecked = (Boolean) values.get(0);
  Boolean secondChecked = (Boolean) values.get(1);

  if (! (firstChecked || secondChecked)) {
    Message message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Check at least one checkbox", null);
    throw new ValidatorException(message);
  }
}
If none checkbox is checked error message is displayed in <h:message for="multi"> tag.

Source code of this component is on github.

Any suggestions, opinions or questions regarding this component are welcome. Have a good time using it :)

3 comments:

Anonymous said...

Well, seems JSF is the answer for a question nobody asked. Seriously, what other java web framework doesn't have built-in support for cross-field validation?

Anonymous said...

Please speak for yourself. JSF is the answer to the question how the arcane experience of working with low level HTTP requests can be improved.

Multi-field validation has always been possible, this just makes it easier.

Anonymous said...

Personally, I find JSF validation model far from being sufficient in case of complex forms, it's field-centric and requires well-known hacks and tricks to achieve inter-field dependencies. This post above present a nice "envelope" for one of such tricks.

I recommend everyone to get familiar with the XForms language - it's way simpler, more elegant, more natural and more powerful.