Insights into Java Server Faces

2009/08/06

MultiValidator component allows validation across multiple input fields

Filed under: JSF — Tags: , — Hanspeter @ 22:52

JSF provides validation, you can even attach multiple validators to a single input component. But it does not (yet?) support validation across multiple input components. To address this shortcoming we developed a MultiValidator component. 

Why would we need a MultiValidator? Without MultiValidator you must implement validation accross multiple fields in your action method (or call it from there) – but at that time (invoke application phase) the entered data has already been set to your backing bean. Ideally we would like only valid data be set to the backing bean – but for that validation accross multiple input fields must be processed in the validation phase – MultiValidator provides exactly this facility.

Special thank goes to Amar, who prepared initial versions of MultiValidator and this article.

Implementation of MultiValidator

MultiValidator extends from UIComponentBase and overrides processValidation to handle the cross field validation during the validation phase of the lifecycle. It has no visual representation – it’s a hidden component – therefore it’s isRendered() method always returns false. When used it must be placed after all of the input components that will be affected by the multi-validation to allow the normal field level validators do it’s work first.

  <h:inputText id="field1" .../>
  <h:inputText id="field2" .../>
  <h:inputText id="field3" .../>
  <mv:multiValidator forIds="field1,field2,field3" validator="#{bean.myMethod}" />
  • forIds is a comma separated list of id’s as references to the components to be used in the multi validate method.
  • validator links to the validate method that you implement in some managed bean – the method signature must be as follows:
  public void myMultiValidate(FacesContext ctx, Collection<UIComponent> components, Collection<Object> values)
     throws ValidatorException { ... }
  • components is a Collection of UIComponents in the same order as defined in forIds attribute.
  • values is a Collection of the submitted values in the same order as defined in the forIds attribute.

MultiValidator implementation consists of the MultiValidator component and a Facelets ComponentHandler that does some validation while setting the forIds TagAttribute and sets the validator MethodExpression (see source links below). And we need a component configuration in faces-config.xml for MultiValidator component and also a Facelets taglib.xml for the tag definition:

faces-config.xml snippet:

  <component><br />
    <component-type>ch.dueni.jsf.ext.multivalidator.MultiValidator</component-type>
    <component-class>ch.dueni.jsf.ext.multivalidator.MultiValidator</component-class>
  </component>

multivalidator.taglib.xml snippet:

  <tag>
    <tag-name>multiValidator</tag-name>
    <component>
      <component-type>ch.dueni.jsf.ext.multivalidator.MultiValidator</component-type>
      <handler-class>ch.dueni.jsf.ext.multivalidator.MultiValidatorTagHandler</handler-class>
    </component>
  </tag>

All these sources may be found on https://github.com/dueni/faces-ext/tree/master/multi-validator

An example validate method could look like this. The validate rule is somewhat simple: the user must enter some input in field 1 and 2 with field 3 empty or only in field 3 with field 1 and 2 empty:

  /**
   * Perform cross validation on fields field1, field2 and field3.
   *
   * @param context
   *          current FacesContext.
   * @param components
   *          collection of involved components.
   * @param values
   *          collection of involved submitted value objects.
   * @throws ValidatorException
   *           if invalid data was detected.
   */
  public void validateFields(FacesContext context, Collection<UIComponent> components,
      Collection<Object> values) throws ValidatorException {
    System.out.println("validateFields is called!");
    for (UIComponent c : components) {
      System.out.println("component id: " + c.getId());
    }
    for (Object v : values) {
      System.out.println("submitted value: '" + v + "'");
    }
    // UIComponent[] comps = components.toArray(new UIComponent[components.size()]);
    String[] inputs = values.toArray(new String[values.size()]);

    // input required
    final String VALIDATE_MSG = "Enter data in field 1 and 2, or only in field 3!"

    if (isEmpty(inputs[0]) && isEmpty(inputs[1]) && isEmpty(inputs[2])) {
      throwValidationException(VALIDATE_MSG);
    }
    // 1 and 2 or 3
    if (!isEmpty(inputs[0]) || !isEmpty(inputs[1])) {
      // input in 1 or 2 detected, now check that 1 and 2 are entered and 3 is
      // empty
      if (isEmpty(inputs[0]) || isEmpty(inputs[1]) || !isEmpty(inputs[2])) {
        throwValidationException(VALIDATE_MSG);
      }
    }
  }

  private void throwValidationException(String text) {
    FacesMessage msg = new FacesMessage(text);
    throw new ValidatorException(msg);
  }

  private boolean isEmpty(String in) {
    return in == null || in.length() == 0;
  }
Advertisements

3 Comments »

  1. Hello!
    Nice component, would come really in handy in my website. I tried implementing it but i get an error because the imports with com.sun.facelets are not found plus some classes like ComponentConfig are not found. I’m i missing something here?
    Thanx

    Comment by Wolfcastle — 2009/08/16 @ 00:48

  2. Hi HP,
    Nice to see the improved version of MultiValidator here.
    In the very initial draft, it was mandatory to add the as a child of last input component.

    But with this new component, must be placed after all of the input components that will be affected by the multi-validation.

    Regards,
    Amar Pandav

    Comment by Amar Pandav — 2010/01/29 @ 20:37


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: