The Problem

If you are developing forms in JSF2 that are using AJAX to show or hide some parts of the form for the best user experience, you surely got into a situation where your ajax call, that was supposed to rerender something, wouldn’t work, because at the same time you wanted to use some JSF validators, or you’ve marked your field as required.

And probably you got there more then once.

If you are not sure what am I talking about let me give you an example.

The Example

The client I am working for at the moment had a need to have:

A Person who might have some Partners

class Person {
   List<Partner> partners;
}
class Partner { 
   String name;
}

And a form, that:

  • has an input for number of Partners a Person has
  • after choosing it the form should generate N number of input fields to capture each Partner’s name

My first take was to have:

  • rich:inputNumberSpinner, so the user can set the number of partners upfront
  • attach f:ajax “onchange” that would update the number of Partners in the list on Person
  • and rerender the panel that would ui:repeat through it showing an input box per each Partner

The view:

<h:panelGroup id="partners">

<rich:inputNumberSpinner value="#{bean.numberOfPartners}">
   <f:ajax execute="partners" render="partners"/>
</rich:inputNumberSpinner>
<ui:repeat value="#{bean.person.partners}" var="partner">
   Partner's name:
   <h:inputText value="#{partner.name}" required="true"/>
</ui:repeat>

</h:panelGroup>

<h:commandButton action="#{bean.save"}/>

And the backing bean:

@Named("bean")
@ViewScoped
public class PersonBean {
   private Integer numberOfPartners;
   private Person person;

   //ADD numberOfPartners getter

   public void setNumberOfPartners(Integer numberOfPartners) {
      this.numberOfPartners = numberOfPartners;

      // if number less then current size - remove
      while (person.partners.size() > numberOfPartners) {
         person.getPartners().remove(person.getPartners().size() - 1);
      }

      // otherwise add
      while (person.getPartners().size() < numberOfRelatedEntities) {
         person.getPartners().add(new Partner());
      }
   }

   public String save() { 
      // persisting logic
   }
}

But this doesn’t work. Why ? The name on the partners is required. So now imagine such a scenario – you’ve chosen 7 partners upfront, started filling them in, but then realized you had only six.

So you try to change the number of partners to 6 but what happens is that the system throws validation error, that the name of the 7th partner is required, and you cannot submit.

What is wrong ? AJAX request, as any other request not only updates the model, but performs the validation. The validation does not make any sense in this case, because we are not saving anything, we just want to update form basing on some conditions.

So I thought it would be cool if I could skip the validation on that particular request. Or actually on any kind of request, but the one when the user finally clicks on SUBMIT to persist the updated Person.

Google had nothing for me.

OnSubmitValidator

So I decided to write something of my own.

I figured that I can write a wrapper validator that could also perform required=”true” like checks but conditionally – only when some parameter is passed together with the submit.

And that’s how OnSubmitValidator from softwaremill-common was created. It’s opensource on apache license – do whatever you want with it.

So now the xhtml looks as follows

<h:panelGroup id="partners">

<rich:inputNumberSpinner value="#{bean.numberOfPartners}">
   <f:ajax execute="partners" render="partners"/>
</rich:inputNumberSpinner>
<ui:repeat value="#{bean.person.partners}" var="partner">
   Partner's name:
   <h:inputText value="#{partner.name}">
      <f:validator validatorId="onSubmitValidator"/>
      <f:attribute name="onSubmitRequired" value="true"/>
   </h:inputText>
</ui:repeat>

</h:panelGroup>

<h:commandButton action="#{bean.save"}>
   <f:param name="performValidation" value="true"/>
</h:commandButton>

Notice two changes:

  • The inputText doesn’t have required=”true” anymore, but uses the new validator with attribute onSubmitRequired set to true (lines 9-10)
  • The saving commmand button has a parameter performValidation which tells the validator that this is the moment to perform the validation (line 17)

If you wanted to use also special validator inside, you could’ve specified it with

<f:attribute name="validatorId" value="VALIDATOR_ID"/>

To sum it up…

So out of the box you get a conditional required=”true” and custom validator’s validation.

A step further would be to somehow wrap also the standard validators like f:validateRange or such.

Or a step even further to hijack javax.validation annotation validation – with @NotNull etc.

If there’s a need for that, I’m gonna think about it. Maybe you have some ideas ?

Conditionally skip JSF validation but perform model updates

7 thoughts on “Conditionally skip JSF validation but perform model updates

  • August 2, 2011 at 2:57 pm
    Permalink

    Thanks man ! Helped me a lot. 😀

    Reply
  • February 24, 2012 at 12:44 pm
    Permalink

    You could have done something like this:
    required=”#{not empty param[‘form:buttonId’]}”
    This way the field will be validated only on the button click.

    Reply
    • April 21, 2014 at 4:32 pm
      Permalink

      Thanks Romero for small tip, really it helped me so much.
      Thanks Again.

      Reply
  • December 13, 2013 at 3:06 pm
    Permalink

    Thank you for sharing. This is an elegant and robust solution for me.

    Reply
  • June 23, 2014 at 8:28 am
    Permalink

    Hello,
    Thanks for lot for the solution.
    I am using Primefaces and facing a similar problem with calling of jsf ‘required’ validation even during an ajax POST request for my dynamic dropdown generation.I want the validations only for my final save button.
    I think that your solution would work for me too but am not sure how can I add the validator and its corresponding attributes to the components as you have shown in lines 9,10 and 17.
    Can you please guide me as to how to do this from the bean, as I am generating my components from the bean and not the xhtml.

    Thanks.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *