JSF 部分屏幕更新处理完整的组件树

Posted

技术标签:

【中文标题】JSF 部分屏幕更新处理完整的组件树【英文标题】:JSF Partial Screen Update processes full component tree 【发布时间】:2015-07-01 07:41:39 【问题描述】:

我需要一些 JSF / PrimeFaces (3.5) 建议。

我们有一个动态表单,表单可以由表单构建器配置,支持 bean 是一个美化的 HashMap 以及一些额外的 getter 和 setter,例如 (getValueAsDate/setValueAsDate)。

我们的一种字段类型允许输入一个数字,当离开该字段时,需要额外的信息并更新部分表单。这似乎一切都很好。

<h:panelGroup id="clientInfo" layout="block" rendered="#field.type == 'CLIENTINFO'">
    <h:outputLabel for="inputClientId">#field.label</h:outputLabel>
    <p:inputText id="inputClientId" maxlength="9" value="#handler.property(field.id).value">
        <p:ajax listener="#handler.fetchClientDetails(field.id)" partialSubmit="true" process="@this" update="@parent,:mainform:msgs"
    </p:inputText>
    <!-- Additional output text elements to display the name, address etc. -->
</h:panelGroup>

最近我们添加了一个字段,允许使用 PrimeFaces 日期选择器组件输入 java.util.Date。添加此字段类型后,部分更新停止工作。

<h:panelGroup id="date" layout="block" rendered="#field.type == 'DATE'">
    <h:outputLabel for="inputDate">#field.label</h:outputLabel>
    <p:calendar id="inputDate" value="#handler.property(field.id).valueAsDate" pattern="dd-MM-yyyy" maxlength="10">
        <f:convertDateTime pattern="dd-MM-yyyy" />
    </p:calendar>
</h:panelGroup>

当检查来自服务器的部分结果时,我们得到如下内容(其中 12345 是在上面的字段中输入的 clientId)。

<partial-response>
    <error>
        <error-name>class java.text.ParseException</error-name>
        <error-message><![CDATA[Unparseable date: "12345"]]></error-message>
    </error>
</partial-response>

问题基本上是为什么它甚至为不是Date 的字段调用getValueAsDate 方法,或者屏幕上什至没有日期字段类型?这可能是我对 JSF 生命周期或部分更新在 JSF/PrimeFaces 中如何工作的遗漏(或误解)。

更新 #1:

刚刚在另一个调试会话中注意到,这不仅发生在部分更新中,而且在最初渲染屏幕时已经发生。似乎所有 EL 表达式一直在计算,这也会在我的支持对象中产生额外的属性(当请求一个属性但它不存在时,它会使用值 null 创建)。

更新 #2:

呈现配置字段的代码使用ui:repeat 和条件ui:fragments(也尝试过h:panelGroups)来呈现配置字段的特定输入元素。

<ui:repeat value=#handler.formFields var="field">
    <ui:fragment rendered="field.type == 'DATE'>
        <!-- Specific fragment for date field -->
    </ui:fragment>
    <ui:fragment rendered="field.type == 'TEXT'>
    </ui:fragment>
    <ui:fragment rendered="field.type == 'REGEXP'>
    </ui:fragment>
    <ui:fragment rendered="field.type == 'CLIENT'>
    </ui:fragment>
</ui:repeat>

尝试了 h:panelGroupui:fragments 两者的组合。

【问题讨论】:

您在面板组周围使用什么? ui:repeat? c:foreach? h:datatable?一些自定义组件?请创建一个mvce 并回答该帖子中的其他相关问题 哪个 JSF 实现/版本?使用 Mojarra impl 时,在 2.1.29 和 2.2.7 中修复了一个非常相似的 &lt;ui:repeat&gt; 错误。尝试至少升级到该版本。相关问题:***.com/q/25594147。如果无法升级,解决方法是使用&lt;c:forEach&gt;&lt;c:if&gt; 而不是&lt;ui:repeat&gt;&lt;ui:fragment&gt;,但可能需要进行其他更改。 手头没有代码,但我相信我们使用的是 2.1.24(尝试了 2.1.27,但这导致屏幕和自定义组件不再工作)。 2.1.25/2.1.26/2.1.27 确实不是推荐的版本。顺便说一句,如果多人对您的帖子发表了评论,请使用@nickname 通知特定用户有关评论回复。直到我浏览回来才看到这个。 @BalusC 升级到版本 2.1.29-01 似乎可以解决这个问题(我记得 2.1.29 有问题)。所以如果你能把你的评论变成一个答案,我可以给你积分。 【参考方案1】:

这可识别为 Mojarra 特定的 &lt;ui:repeat&gt; 错误,报告为 issue 3215,已在 2.2.7 中修复,并在 2.1.29 中按照 issue 3221 进行反向移植。简而言之,问题归结为&lt;ui:repeat&gt; 在保存自己的状态时不尊重其EditableValueHolder 孩子的状态,因此基本上表现得好像这些孩子的rendered 属性在状态保存期间从未受到尊重。以下相关问答详细说明了其他后果之一:PropertyNotFoundException on conditionally rendered subclasses in ui:repeat。

鉴于您使用的是 Mojarra 2.1.x,最好的选择是至少升级到 2.1.29。如果您的环境允许(Servlet 3.0 等),也应该可以升级到最新的 2.2.x。

替代方案是将 Mojarra 替换为 MyFaces,或将 &lt;ui:repeat&gt;&lt;ui:fragment&gt; 替换为 &lt;c:forEach&gt;&lt;c:if&gt;

【讨论】:

以上是关于JSF 部分屏幕更新处理完整的组件树的主要内容,如果未能解决你的问题,请参考以下文章

JSF Lifecycle

是否可以在 JSF 中实现组件的动态树?

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

是否可以使用 JSF ajax 更新非 JSF 组件(纯 HTML)?

将新组件动态添加到 JSF 组件树时重复 id

JSF2:在运行时构建 JSF2 视图(整个组件树)