为什么在未呈现构面时,我的JSF复合构面的验证正在完成

时间:2016-02-08 21:46:56

标签: jsf primefaces composite-component facet conditional-rendering

我遇到一个问题,即即使我没有渲染复合材料,也会触发复合面的验证。

我将问题解决了以下准系统代码。

这是复合entityDetailPanel

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:composite="http://java.sun.com/jsf/composite"
            xmlns:p="http://primefaces.org/ui"
            xmlns:common="http://java.sun.com/jsf/composite/common">

<composite:interface>
    <composite:attribute name="prefix" required="true" />
    <composite:facet name="lowerPanel"/>
</composite:interface>

<composite:implementation>

    <h:form id="#{cc.attrs.prefix}entityDetailForm2" 
            styleClass="#{cc.attrs.prefix}EntityDetailPanelForm #{cc.attrs.prefix}Listener" >

        <p:messages id="#{cc.attrs.prefix}messages" autoUpdate="true" closable="true"/>

        <p:commandButton 
            value="SAVE" 
            update="@(.#{cc.attrs.prefix}Listener), @(.#{cc.attrs.prefix}EntityDetailPanelForm}"/>

        <composite:renderFacet name="lowerPanel" rendered="false"/>
    </h:form>

</composite:implementation>
</ui:composition>

这是调用:

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:f="http://java.sun.com/jsf/core"
            xmlns:p="http://primefaces.org/ui"
            xmlns:common="http://xmlns.jcp.org/jsf/composite/common">

    <common:entityDetailPanel id="foo" prefix="Instruments">

        <f:facet name="lowerPanel">
            <!--  <p:inputText id="assetClassPrompt" required="true"  requiredMessage="Why do we get this message?"/>-->

            <p:selectOneMenu id="assetClassPrompt" required="true"  requiredMessage="Why do we get this message?"
                             value="#{instrumentController.selectedData.assetClass}">
                 <f:selectItem itemLabel="foo" itemValue="foo"/>
                 <f:selectItem itemLabel="bar" itemValue="bar"/>
            </p:selectOneMenu>
        </f:facet>
    </common:entityDetailPanel>

</ui:composition>

组合框没有显示在屏幕上(因为它没有渲染),但为什么我会对未渲染的内容进行验证呢?

这是我点击“保存”按钮时看到的内容:

enter image description here

更奇怪的是,即使在没有那个组合框的复合材料的其他调用中,我也看到了这个验证错误。

我还注意到,如果我在<messages>标记上没有包含唯一ID,则使用复合的消息将显示在合成的其他用途中。

这是PrimeFaces还是JSF错误,还是我错过了什么?

您可能会注意到我有一个已注释的<inputText>标记。值得一提的是,当我替换<selectOneMenu>并将其替换为<inputText>时,我不再看到问题。

我认为可能有助于澄清我试图解决的更大问题。

我想创建类似于<p:layout>的东西,它既包含固定元素(对于复合材料的所有用途),也包含以参数方式传递的非固定元素/面板(对于组件的每次使用)。

这是一个屏幕截图,其中read中指示的项目随着复合的每次调用而变化。其他所有内容始终存在于复合材料的所有调用中。

如您所见,参数为:

  1. 按钮面板(按钮因具体情况而异)
  2. 要添加到表单末尾的一些其他字段(可能包含验证
  3. 整个下方面板(可能包含验证)
  4. Sample of the composite

    值得一提的是,所有这些事情都是一起验证的(对于&#34; SAVE&#34;按钮),所以最好让<form>标签在复合材料中输出(包括作为参数传入的面板)。

1 个答案:

答案 0 :(得分:4)

这个问题是双重的。

首先,<cc:renderFacet>从未设计为以这种方式工作。它does not支持rendered属性。它以某种方式工作是因为facet在内部被重新解释为UIPanel组件,并且所有属性(错误地)自动从标签继承。你不应该依赖它。渲染响应期间错误地考虑了rendered属性,导致其“工作”的混乱行为。这在技术上是JSF实现中的一个错误。在回发期间,属性(正确)继承,导致您观察到的麻烦。这些组件仍然被解码和验证“尽管”它们没有被渲染。

其次,<p:inputText>UIInput延伸,在验证之前检查是否有任何提交的值。提交的值null被解释为完全没有表单中的输入字段,因此它被跳过。提交的空字符串值作为空值进行处理,因此已经过验证。但是,<p:selectOneMenu>已覆盖标准UIInput行为,并将null视为与空字符串相同的方式。即使提交的值为null(这意味着输入字段根本不在表单中),它仍然在被验证。这在技术上是PrimeFaces方面的一个错误。

你的意图至少是明确的:有条件地渲染一个方面。在Facelets编译时(这是视图构建时间之前的一个步骤)期间评估<cc:xxx>标记,因此使用JSTL <cc:renderFacet>有条件地构建<c:if>也将无法工作。

您最好的选择是将“渲染下方面板”重新定义为复合属性,并创建一个支持组件,以便在将该属性添加到视图后将其显式复制到构面中。

<cc:interface componentType="entityDetailPanelComposite">
    ...
    <cc:facet name="lowerPanel" />
    <cc:attribute name="renderLowerPanel" type="java.lang.Boolean" default="false" />
</cc:interface>
<cc:implementation>
    <f:event type="postAddToView" listener="#{cc.init}" />
    ...
    <cc:renderFacet name="lowerPanel" />
    ...
</cc:implementation>

@FacesComponent("entityDetailPanelComposite")
public class EntityDetailPanelComposite extends UINamingContainer {

    public void init() {
        UIComponent lowerPanel = getFacets().get("lowerPanel");
        ValueExpression renderLowerPanel = getValueExpression("renderLowerPanel");

        if (renderLowerPanel != null) {
            lowerPanel.setValueExpression("rendered", renderLowerPanel); // It's an EL expression.
        } else {
            lowerPanel.getAttributes().put("rendered", getAttributes().get("renderLowerPanel")); // It's a literal value, or the default value.
        }
    }

}

这有额外的好处,您可以从客户端指定它。

<my:entityDetailPanel ... renderLowerPanel="true" />