JSF 2.0: How to skip JSR-303 bean validation?
Asked Answered
C

4

12

How to skip JSR-303 Bean validation with JSF, when a button is clicked?

A bit lengthy question to explain a few approaches... Consider a list in a form:

<h:form id="form">
    <h:commandButton value="Add row">
        <f:ajax execute="foo" listener="#{bean.add()}" render="foo" />
    </h:commandButton>
    <h:dataTable id="foo" var="foo" value="#{bean.foos}">
        <h:column>
            Name: <h:inputText id="field" value="#{foo.name}" required="true" />
            <h:messages for="field" />
        </h:column>
        <h:column>
            <h:commandButton value="Remove">
                <f:ajax execute=":form:foo" listener="#{bean.remove(foo)}" render=":form:foo" />
            </h:commandButton>
        </h:column>
    </h:dataTable>
</h:form>

When user clicks add or remove row, the action should execute without validation. The problem is, JSF re-renders the whole list and tries to validate it. If there are draft changes that don't validate, validation errors occur and the listener method is never called (as failed validation prevents that). However, adding immediate="true" into f:ajax allows method to execute despite of validation errors. However, validation errors still occur and are shown here.

I see two options:

1) Use immediate="true" and do not show validation errors

For non-validating buttons, set immediate="true" and for h:messages do:

<h:messages rendered="#{param['SHOW_VALIDATION']}" />

Then set Save-button (that should actually try to save the form) to send that parameter:

<h:commandButton>
    <f:param name="SHOW_VALIDATION" value="true" />
</h:commandButton>

This causes validation occur, but messages are simply not shown except when SHOW_VALIDATION parameter is present.

2) Declare validation in facelets conditionally:

<h:inputText>
    <f:validateRequired disabled="#{!param['VALIDATE']}" />
</h:inputText>

And Save-button:

<h:commandButton>
    <f:param name="VALIDATE" value="true" />
</h:commandButton>

This causes fields to validate only when the VALIDATE parameter is present (=when the Save-button has been pressed).

But these seem sort of a hacks. How can I simply use JSR-303 Bean Validation but skip it when declared?

Carincarina answered 31/1, 2011 at 15:14 Comment(1)
Well, I haven't tried any other validation solutions than JSR-303 and facelets with JSF. I think this issue is JSF related and not JSF-303's fault - it's like they forgot to "natively" support skipping validation in JSF. There seems to be lots of confusion around this...Carincarina
H
9

Set your event handlers as immediate=true and call FacesContext.renderResponse() before exiting them.

UPDATE:

Modifications in your example form:

<h:form id="form">
    <h:commandButton value="Add row">
        <!-- Added immediate="true" to call bean.add() before validation phase -->
        <f:ajax execute="foo" listener="#{bean.add()}" render="foo" immediate="true"/>
    </h:commandButton>
    <h:dataTable id="foo" var="foo" value="#{bean.foos}">
        <h:column>
            Name: <h:inputText id="field" value="#{foo.name}" required="true" />
            <h:messages for="field" />
        </h:column>
        <h:column>
            <h:commandButton value="Remove">
                <!-- Added immediate="true" to call bean.remove() before validation phase -->
                <f:ajax execute=":form:foo" listener="#{bean.remove(foo)}" render=":form:foo" immediate="true"/>
            </h:commandButton>
        </h:column>
    </h:dataTable>
</h:form>

Modifications in your bean code:

...
public void add() {
    // Your add() code
    ...
    // Added FacesContext.renderResponse() call to skip to render response phase
    FacesContext.getCurrentInstance().renderResponse();
}
...
public void remove() {
    // Your remove() code
    ...
    // Added FacesContext.renderResponse() call to skip to render response phase
    FacesContext.getCurrentInstance().renderResponse();
}
...
Honeyhoneybee answered 31/1, 2011 at 15:30 Comment(5)
How do you do that in practice? Can you give an example?Carincarina
To make it clear: Doing this makes JSF skip phases number 3-5 (process validations, update model, invoke application) and goes directly to phase 6 (render response). This is ok, as the result is kinda same as if validations failed in phase 3 and JSF would skip to phase 6. Correct me if wrong. The next step from this would be to generalize/componentize this so calling FacesContext.getCurrentInstance().renderResponse() wouldn't be needed. Picking as the accepted answer, thanks for the nice solution! :)Carincarina
I tried the above and in general it works fine. My case is the following - I use a command button to dynamically add a number of text boxes to the page. If I use immediate="false", page validation will prevent the update, and will not show up as well. If I use immediate="true", and modify some inputs' values before doing the AJAX request, the modifications will not be submitted. Any ideas how can I achieve both reading changed input data in the callback and do not trigger the validation at the same time? Thanks in advancePieria
I have the same issue. I would like the model to be updated, but skipping validation. I have a form with different tabs. I add tabs when selecting different checkboxes. Ajax render the p:tabView. But I don't want to loose the info already inputted or changed inside the tabs.Yaupon
You can set immediate="true" on inputText and it should change the values before invoking any action.Honeyhoneybee
M
8

You can disable the bean validation with the tag f:validateBean that has an attribute disabled.

Example:

<h:inputText value="#{bean.name}">
   <f:validateBean disabled="#{anotherBean.flag}"/>
</h:inputText>
Maleeny answered 9/2, 2011 at 2:22 Comment(2)
This is good information as well. However, it is still a bit cumbersome setting this for every field in the form...Carincarina
You can wrap multiple input components with f:validateBean too, like shown here: https://mcmap.net/q/18340/-how-can-i-skip-bean-validation-if-all-fields-for-the-entity-are-emptyHonestly
W
2

We have just published a solution for this problem. The full detail is here: http://www.springfuse.com/2011/12/15/skip-jsf-validation-depending-on-action.html

In short, it leverages the binding attribute of the validateBean tag. Doing so enable you to intercept the validate method and decide, based on the button clicked, whether to let it go through or not.

Werth answered 16/12, 2011 at 14:56 Comment(0)
I
-1

Situation: In the page code there is a big form with a lot of input fields. There are several buttons each pointing to separate action in the separate bean.

A solution that worked for me...

  1. In the page code: add immediate="true" to the button and provide an id for the input fields that You want to use in the bean.
 <p:inputText id="someHtmlInputId"
               maxlength="30" 
               value="#{beanName.attr}" />


 <p:commandButton id="someHtmlButtonId"
                   actionListener="#{beanName.doStuff}" 
                   value="Button Label"
                   ajax="false"
                   immediate="true"/>
  1. In the bean doStuff method get the parametes by the name fragment couse before the input name JSF put some other identifiers and the resulting parameter name may look like this: someFormId:j_idt54:someHtmlInputId
Map<String, String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
if (params != null) {
  for (String k: params.keySet())
      if (k.indexOf("someHtmlInputId") != -1)
          params.get(k); // This is Your input parameter value waiting to be processed ;)
}
Ivanna answered 3/4, 2012 at 10:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.