为啥我的 JSF 复合方面的验证在未呈现方面时完成

Posted

技术标签:

【中文标题】为啥我的 JSF 复合方面的验证在未呈现方面时完成【英文标题】:Why are validations from my JSF composite facet being done when the facet is not rendered为什么我的 JSF 复合方面的验证在未呈现方面时完成 【发布时间】:2016-05-18 17:24:45 【问题描述】:

我有一个问题,即使我不渲染复合材料,来自复合材料方面的验证也会被触发。

我将问题简化为以下准系统代码。

这里是复合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.prefixentityDetailForm2" 
            styleClass="#cc.attrs.prefixEntityDetailPanelForm #cc.attrs.prefixListener" >

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

        <p:commandButton 
            value="SAVE" 
            update="@(.#cc.attrs.prefixListener), @(.#cc.attrs.prefixEntityDetailPanelForm"/>

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

组合框没有显示在屏幕上(因为它没有被渲染),但是为什么我会得到一个没有被渲染的东西的验证呢?

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

更奇怪的是,即使在没有该组合框的其他复合调用中,我也会看到此验证错误。

我还注意到,如果我在 &lt;messages&gt; 标记上不包含唯一 ID,则来自复合材料的一种用途的消息将显示在复合材料的其他用途中。

这是 PrimeFaces 或 JSF 错误,还是我遗漏了什么?

您可能会注意到我有一个注释掉的&lt;inputText&gt; 标记。值得一提的是,当我将&lt;selectOneMenu&gt; 替换为&lt;inputText&gt; 时,我不再看到问题。


我认为这可能有助于阐明我正在尝试解决的更大问题。

我想创建类似于&lt;p:layout&gt; 的东西,它既有固定元素(用于复合材料的所有用途),也有非固定元素/面板,它们以参数方式传入(用于组件的每次使用)。

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

如你所见,参数为:

    按钮面板(按钮因上下文而异) 要添加到表单末尾的一些附加字段(可能包含验证) 整个下部面板(可能包含验证)

值得一提的是,所有这些东西都是一起验证的(对于“SAVE”按钮),因此最好将&lt;form&gt; 标签放在复合输出中(包括作为参数传入的面板)。

【问题讨论】:

【参考方案1】:

这个问题有两个方面。

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

其次,&lt;p:inputText&gt; 扩展自 UIInput,它在验证之前检查是否有 任何 提交值。 null 的提交值被解释为表单中完全没有输入字段,因此它被跳过。一个空字符串的提交值作为一个空值被插入,所以它被验证了。但是,&lt;p:selectOneMenu&gt; 覆盖了标准的UIInput 行为,并将null 视为空字符串。即使提交的值为null(这意味着输入字段根本不在表单中),它仍在验证中。这在技术上是 PrimeFaces 方面的一个错误。

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

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

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

【讨论】:

BalusC,感谢您出色的解释和解决方案。我仍然对某事感到困惑。在我的精简版本中引入了带有无效渲染属性的 标记。它不在原始程序中。从组合中删除 标记,您仍然可以得到验证。此外,最初的问题是我在每次使用复合材料时都会遇到验证错误。即使在 lowerPanel 方面没有验证的使用中。为什么会发生这种情况,除了您的解决方案之外,还有更简单的方法来避免这种情况吗?谢谢。 The facets are also processed during decode。如果您在编码期间实际上从不使用复合刻面,那么这个构造会很尴尬,是的,但它就是这样。 好的。我想在这里做的事情似乎并不稀奇。实际上,我想创建类似于“布局”的东西,在其中我将面板(带有验证元素)作为参数(准面)传递。听起来我不能用复合材料来做到这一点,因为在解码过程中会处理这些方面。正确的?我是否可以通过编写 @FacesComponent 来做到这一点,或者我会遇到同样的问题吗?这是最好的方法吗?我必须相信它可以通过某种方式完成,因为我正在尝试做的与 所做的没有太大区别(w layoutUnits 像参数一样?) IMO 您高估了复合组件。复合组件的一个理想用例示例是单个可重用输入组件,它与单个(自定义)bean 属性相关联。例如。文件上传和图像裁剪器合二为一,与com.example.Image 属性&lt;my:inputFileCropImage value="#bean.image"&gt; 相关联。或者三个与(Local)Date 属性&lt;my:inputDate value="#bean.date"&gt; 相关的d/m/y 下拉列表。我想你只想要一个带有定义/插入的标签文件。另见 a.o. ***.com/q/6822000 好的。您显然是正确的,这是一个模板而不是复合材料。我非常感谢您的投入,如果您有这样的设置,我会捐款。

以上是关于为啥我的 JSF 复合方面的验证在未呈现方面时完成的主要内容,如果未能解决你的问题,请参考以下文章

JSF拒绝处理深层嵌套的复合组件。不,真的:“JSF1098:[...]这浪费了处理器时间[...]”

JSF 2 复合组件渲染问题

JSF2 - 为啥渲染响应不重新渲染组件设置?

如何对 jsf 复合组件中的集合属性进行 bean 验证,约束不会触发

如何将验证器属性从 JSF2 复合组件传递给 h:inputText?

为啥复合主键还在?