Why does <o: validateAll> behave differently than other validators?
I use OmniFaces <o: validateAll> authentication module to check the number of input components. This works fine until I put it in RichFaces <rich: tabPanel> . When I do this and leave the fields blank, the check fails (as expected), but the active tab changes regardless of the failed check. Other validators I tried to prevent tabPanel switching to another tab when validation fails.
What could be the reason for this?
I am currently using OmniFaces 2.1 and RichFaces 4.5.17.Final with Mojarra 2.2.12 on Wildfly 9.0.2.
Here is the XHTML code to reproduce the problem:
<ui:composition xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:o="http://omnifaces.org/ui" xmlns:rich="http://richfaces.org/rich"> <h:form id="form"> <rich:messages /> <rich:tabPanel id="tabPanel"> <rich:tab id="tab1" header="Tab 1"> <h:inputText id="myDouble" value="#{someDoubleVal}"> <f:validateDoubleRange minimum="1.0" maximum="2.0"/> </h:inputText> <o:validateAll id="allValid" components="myDouble" message="Missing value!" /> </rich:tab> <rich:tab id="tab2" header="Tab 2"> Just another tab to switch. </rich:tab> </rich:tabPanel> </h:form> </ui:composition> Enter a value outside 1.0 and 2.0 and try switching to tab 2 to see the expected behavior caused by <f:validateDoubleRange> : a face message is displayed and the first tab is still active.
Leave the field blank and try switching to tab 2 to see the <o:validateAll> behavior: the check seems unsuccessful (a face message is displayed), but tab 2 is activated.
Update: The described behavior applies to switchType="ajax" (default), as well as switchType="server" . In both cases, the tab bar feeds the included inputs, so from the point of view of users, the tab switch seems to be the same as <h:commandButton> submit (technically there may be differences, I don’t know the details of the implementation of the tab).
If I perform a tab switch using a regular <h:commandButton> with <f:setPropertyActionListener> , then <o:validateAll> behaves the same as other validators, i.e. the tab switch is not executed due to a validation error.
<rich:tabPanel id="tabPanel" activeItem="#{bb.activeTab}"> ... <rich:tab id="tab1" name="tab1" header="Tab 1"> ... <h:commandButton value="submit"> <f:setPropertyActionListener value="tab2" target="#{bb.activeTab}" /> </h:commandButton> ... </rich:tab> </rich:tabPanel> Note: This is just a minimalist example showing problematic behavior. In my real code, I have not only one component tested with <o:validateAll> , and I actually bind the input values with bean support. The observed behavior is exactly the same.
The problem is twofold.
The first problem is that <o:validateAll> does not explicitly call context.renderResponse() when the validation fails, and leaves this job in JSF, which will implicitly invoke it during the validation phase when at least one input component was invalidated after <o:validateAll> started, or otherwise during subsequent phases of update model values.
The second problem is that the <rich:tabPanel> tab switching event is queued for the update model value phase, and not for invoking the application phase. I’m not sure why the guys from RichFaces designed it this way, but the consequence is that the tab switching event is fired anyway even when the validation is found to fail only in the update model values phase.
When you move <o:validateAll> to at least one associated input component, then JSF will implicitly call context.renderResponse() during the validation phase and therefore completely skip the update model values phase, and therefore the tab wait event <rich:tabPanel> t there is an opportunity for a call.
I installed it in OmniFaces 2.6-SNAPSHOT according to issue 322 . When using OmniFaces 2.6 or later, there should no longer be a place where <o:validateAll> is placed in the tree to achieve the desired behavior of the <rich:tabPanel> tab switch event so that it is not called.