Validate a h:selectBooleanCheckbox only if all the other inputs are valid

Viewed 104

I have a register form in JSF and I want the validate if the new user agreed with the terms and conditions only after all the other fields from the form are valid. Is there any way to validate this in JSF? Here is a sample of my code.

    <h:form id="registerForm">
        <table>

            //Other fields
            <tr>
                <td><h:outputText value="Username " /></td>
                <td><h:inputText id="fusername"
                        value="#{registerUser.username}">
                        <f:validator for="fusername" validatorId="usernameValidator" />
                    </h:inputText></td>
                <td><h:message for="fusername" style="color:red" /></td>
            </tr>

            <tr>
                <td><h:outputText value="Password: " /></td>
                <td><h:inputSecret id="fpassword"
                        value="#{registerUser.password}">
                        <f:validator for="fpassword" validatorId="passwordValidator" />
                    </h:inputSecret></td>
                <td><h:message for="fpassword" style="color:red" /></td>
            </tr>

            <tr>
                <td><h:outputText value="Repeat Password " /></td>
                <td><h:inputSecret id="repeatPassword"
                        value="#{registerUser.repeatPassword}">
                            <f:validator for="repeatPassword" validatorId="repeatPasswordValidator"/>
                            <f:attribute name="originalPassword" value="fpassword" /> 
                        </h:inputSecret></td>
                <td><h:message for="repeatPassword" style="color:red" /></td>
            </tr>
            <tr>
                <td><h:selectBooleanCheckbox
                        value="#{registerUser.agreeTermsAndConditions}" required="true"/></td>
                <td>I agree with the terms and condition.</td>
            </tr>


            <tr>
                <td />
                <td><h:commandButton action="#{registerUser.saveUser}"
                        value="Register" /></td>
            </tr>

        </table>
    </h:form>


</f:view>

2 Answers

Provided that the "I agree" checkbox is the last UIInput component of the form (in the average UI designs it actually usually is!), then you can just do so:

<h:selectBooleanCheckbox ... required="#{not facesContext.validationFailed}" />

Namely, JSF processes the input components from top to bottom, in exactly the same order as they are declared in the view. When JSF is about to process the checkbox, then it will re-evaluate the required attribute. And in this case, when no other UIInput components above the checkbox have caused a validation failure, then FacesContext#isValidationFailed() will return false and therefore the component will be considered as required.

But there's another problem: the required attribtue of the <h:selectBooleanCheckbox> has by default no effect at all. Because, when the checkbox is unchecked, then the submitted value will become false, which is not null. The omnifaces.RequiredCheckboxValidator solves this peculiar problem.

So, all in all:

<h:selectBooleanCheckbox ... required="#{not facesContext.validationFailed}">
    <f:validator validatorId="omnifaces.RequiredCheckboxValidator" />
</h:selectBooleanCheckbox>

You could manually validate on your checkbox in your save method, because there you've passed validation. Note that you want to set an ID to your checkbox to find it.

public void saveUser(){
  if (!agreeTermsAndConditions) {
    FacesContext context = FacesContext.getCurrentInstance();
    // Tell JSF validation has failed
    context.validationFailed();
    // Get the component
    UIComponent component = context.getViewRoot().findComponent("registerForm:agreeTermsAndConditions");
    // Set it to invalid
    ((EditableValueHolder)component).setValid(false);
    // Create a message
    FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "You must agree with the terms and conditions.", null);
    // Register the message for the component
    context.addMessage(component.getClientId(), message);
  }
  else {
    // Agreed, so save the user here
    ...
  }
}

And in your view add the ID used to find the component and a h:message to display the set message.

<h:form id="registerForm">
  <table>
    ...
    <tr>
      <td><h:selectBooleanCheckbox id="agreeTermsAndConditions"
                                   value="#{registerUser.agreeTermsAndConditions}"/></td>
      <td>I agree with the terms and condition.</td>
      <td><h:message for="agreeTermsAndConditions" style="color:red" /></td>
    </tr>
    ...
  </table>
</h:form>

If you simply want to make the checkbox required regardless of other fields, see:

Related