为什么 <o:validateAll> 的行为与其他验证器不同?

Why does <o:validateAll> behave different than other validators?

我用OmniFaces' <o:validateAll> validator to validate a number of input components. This works fine as long as I do not put it into a RichFaces <rich:tabPanel>。当我这样做并将字段留空时,验证失败(如预期的那样),但活动选项卡已更改,无论验证失败如何。每当验证失败时,我尝试过的其他验证器都会阻止 tabPanel 切换到另一个选项卡。

这可能是什么原因?

我目前在 Wildfly 9.0.2 上使用 OmniFaces 2.1 和 RichFaces 4.5。17.Final 和 Mojarra 2.2.12。

这是重现问题的 XHTML 代码:

<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>

输入 1.0 和 2.0 以外的值并尝试切换到选项卡 2 以查看由 <f:validateDoubleRange> 触发的预期行为:显示面孔消息并且第一个选项卡仍处于活动状态。

将输入留空并尝试切换到选项卡 2 以查看 <o:validateAll> 的行为:验证似乎失败(显示面孔消息),但选项卡 2 已激活。

更新: 所描述的行为适用于 switchType="ajax"(默认值)以及 switchType="server"。在这两种情况下,选项卡面板都执行包含输入的提交,因此从用户的角度来看,选项卡开关似乎与 <h:commandButton> 提交相同(技术上可能存在差异,我不不知道选项卡面板的实现细节)。

如果我通过带有 <f:setPropertyActionListener> 的常规 <h:commandButton> 执行制表符切换,<o:validateAll> 的行为方式与其他验证器相同,即制表符切换不是由于验证错误而执行。

<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>

注意:这只是一个显示问题行为的简单示例。在我的真实代码中,我不仅有一个由 <o:validateAll> 验证的组件,而且我确实将输入值与一个支持 bean 相关联。观察到的行为完全相同。

问题有两个。

第一个问题是,<o:validateAll> 不会在验证失败时显式调用 context.renderResponse() 并将此工作留给 JSF,JSF 将在验证阶段至少找到一个输入组件时隐式调用它在 <o:validateAll> 具有 运行 之后无效,否则在后续更新模型值阶段无效。

第二个问题是,<rich:tabPanel> 选项卡切换事件排队等待更新模型值阶段而不是调用应用程序阶段。我不确定为什么 RichFaces 的人会那样设计它,但结果是即使仅在更新模型值阶段发现验证失败,标签切换事件仍然会被触发。

当您将 <o:validateAll> 移动到至少一个关联的输入组件之前时,JSF 将在验证阶段隐式调用 context.renderResponse() 并因此完全跳过更新模型值阶段以及排队的 <rich:tabPanel> 选项卡切换事件将没有机会被调用。

我已根据 issue 322 在 OmniFaces 2.6-SNAPSHOT 中修复它。当使用 OmniFaces 2.6 或更新版本时,<o:validateAll> 放置在树中的哪个位置以实现不调用 <rich:tabPanel> 选项卡切换事件的预期行为不再重要。