嵌套 JSF 复合组件中的 AJAX
Posted
技术标签:
【中文标题】嵌套 JSF 复合组件中的 AJAX【英文标题】:AJAX within nested JSF Composite Component 【发布时间】:2012-07-11 04:55:46 【问题描述】:我在使用 Mojarra 2.0.3 和 PrimeFaces 的嵌套复合组件中正确触发 AJAX 调用时遇到了一些麻烦。
子复合组件看起来像这样:
<cc:interface componentType="therapy">
<cc:attribute name="therapyType" type="java.lang.String" required="true"/>
<cc:attribute name="patientId" type="java.lang.String" required="true"/>
<cc:attribute name="showHistory" type="java.lang.Boolean" required="false" default="true"/>
<cc:attribute name="width" type="java.lang.String" required="false" default="350px"/>
<cc:attribute name="maxHistory" type="java.lang.String" required="false" default="3"/>
<cc:attribute name="collectDoctor" type="java.lang.Boolean" required="false" default="false"/>
<cc:attribute name="collectCareDate" type="java.lang.Boolean" required="false" default="false"/>
<cc:attribute name="important" type="java.lang.Boolean" requred="false" default="false"/>
</cc:interface>
<cc:implementation>
<script>
function #cc.clientIdToggle()
$("##cc.clientId_newbutton").toggle();
$("##cc.clientId_savebuttons").toggle();
if(#cc.attrs.collectDoctor)
$("##cc.clientId_doctor").toggle();
if(#cc.attrs.collectCareDate)
$("##cc.clientId_care").toggle();
$("##cc.clientId_newTherapy").toggle(50);
function #cc.clientIdrowHighlight(event)
if(event.status == 'begin')
$("##cc.clientId_loader").toggle();
if(event.status == 'success')
$("##cc.clientId\\:histTable tr:eq(1)").effect("highlight", color:"#FED17A", easing:"easeInCubic", 2000);
$(function()
if(#cc.attrs.important)
$("div[class~='ui-panel-titlebar'][id^='#cc.clientId']").css("background", "#FED17A");
);
</script>
<h:form prependId="false">
<p:panel styleClass="mcoPanel" style="width:#cc.attrs.width;">
<f:facet name="header">
<h:panelGroup>
<span id="#cc.clientId_title">#cc.myType.word</span>
<span id="#cc.clientId_newbutton" class="mcoPanel-controls">
<span onclick="#cc.clientIdToggle();">
<h:graphicImage name="page_new.gif" library="images"/>
</span>
</span>
<span id="#cc.clientId_savebuttons" class="mcoPanel-controls" style="display:none;">
<span id="#cc.clientId_loader" style="display:none;">
<h:graphicImage name="ajax-loader.gif" library="images" />
</span>
<h:commandLink action="#cc.saveNewTherapy">
<f:ajax execute="newOnTherapy newExemption newDoctor newCareDate" render="@form" onevent="#cc.clientIdrowHighlight"/>
<h:graphicImage name="action_save.gif" library="images"/>
</h:commandLink>
<span onclick="#cc.clientIdToggle();">
<h:graphicImage name="page_cross.gif" library="images"/>
</span>
</span>
</h:panelGroup>
</f:facet>
<div id="#cc.clientId_newTherapy" class="mcoPanel-new" style="display:none;">
<h:outputLabel for="newOnTherapy" value="Satisfied:" style="position:relative; top: -10px;"/>
<p:selectOneMenu id="newOnTherapy" label="Satisfied" value="#cc.newOnTherapyValue" style="width: 60px;">
<f:selectItem itemLabel=""/>
<f:selectItems value="#cc.yesNoList"/>
</p:selectOneMenu>
<br/>
<h:outputLabel for="newExemption" value="Exemption:" style="position:relative; top: -10px;"/>
<p:selectOneMenu id="newExemption" value="#cc.newExemption" style="width: 150px;">
<f:selectItems value="#cc.exemptions"/>
</p:selectOneMenu>
<span id="#cc.clientId_doctor" style="display:none">
<br/>
<h:outputLabel for="newDoctor" value="Doctor:"/>
<p:inputText id="newDoctor" value="#cc.newDoctor"/>
</span>
<span id="#cc.clientId_care" style="display:none">
<br/>
<h:outputLabel for="newCareDate" value="Care Date:"/>
<p:calendar id="newCareDate" label="Care Date" value="#cc.newCareDate" showButtonPanel="true">
<f:converter converterId="dateOfBirthConverter"/>
</p:calendar>
</span>
</div>
<h:messages id="#cc.clientId_messages" errorClass="errorMessage"/>
<p:dataTable id="histTable" value="#cc.history" var="item" rendered="#cc.attrs.showHistory">
<!-- Table Output -->
</p:dataTable>
</p:panel>
</h:form>
父复合组件看起来像这样:
<cc:interface>
<cc:attribute name="title" type="java.lang.String" required="true"/>
</cc:interface>
<cc:implementation>
<h:outputScript name="containerpanel.js" library="js/components" target="head"/>
<p:panel toggleable="true" styleClass="contentPanel" toggleSpeed="500" style="width:1100px;">
<f:facet name="header">
#cc.attrs.title
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentSaveAll">
Save All
</div>
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentExpandAll">
++
</div>
<div class="ui-panel-titlebar-icon ui-corner-all ui-state-default contentCollapseAll">
- -
</div>
</f:facet>
<cc:insertChildren>
<!-- Child components go here -->
</cc:insertChildren>
</p:panel>
</cc:implementation>
该实现应该允许容器组件内有任意数量的子组件。
如果将子组件放置在 Container 组件之外的页面上,则子组件会发送 ajax 请求并完美地更新其内部组件。另外,如果我使用容器的相同结构,但不是真正的复合组件,一切正常。
一旦我将子组件放置为 Container 组件的子组件,事情就会变得不稳定。子组件中没有 ajax 调用正确触发(即保存 commandLink)使用 firebug 帮助调试,我已经能够确定以下内容:
似乎正在发送 ajax 发布请求。 "begin" onevent 正在触发,在 firebug 中查看控制台会显示正在向服务器发送一个帖子,并发送回正确的组件 ID 和值。 未调用支持 bean 中的操作。我有一个打印语句作为第一行,它没有打印。 ajax 请求似乎正在正确完成,“成功”onevent 正在触发。 页面上没有更新任何内容。表单不会刷新,页面顶部的 p:messages 对象也不会刷新。显然,我可以在没有 Container 组件的情况下做到这一点,但我当然更希望能够利用代码重用。我是否在这里遗漏了一些明显的东西,我需要做些什么才能让 ajax 正常工作?
【问题讨论】:
【参考方案1】:我在使用 MyFaces 2.1.6 时遇到了完全相同的问题。 我无法使用 CC 界面中的 ClientBehavior 从嵌套的复合组件进行 AJAX 调用。
就像moneyT 写的那样,服务器被通知了ajax 标记中指定的事件,但是当生命周期进入第5 阶段- 调用应用程序时,事件为空并且没有调用任何侦听器。它越来越像 MyFaces 实现中的一个错误。
编辑:我找到了另一种解决方案,它比在复合组件中硬编码 ajax 好得多。我已将外部复合组件重构为 facelet。它失去了复合材料的一些怪癖,但它完成了工作。它还可以重复使用。
我发现的唯一解决方案(与其说是解决方案,不如说是一种解决方案)是在复合组件中硬编码 ajax,而不是使用 ClientBehavior,如下所示:
<composite:implementation>
<h:panelGroup layout="block" id="listBox" class="list-box-scroll-pane" style="height:#cc.attrs.visibleLines*27 + 5px; width:#cc.attrs.widthpx">
<h:inputText id="submit-value" style="visibility: hidden; width:0px; height: 0px;" value="#cc.attrs.property">
<f:ajax event="change"
render="carModel"
listener="#carDetailsMediator.changeCarMake"
onevent="initComboboxes"/>
</h:inputText>
<ui:repeat value="#cc.attrs.items" var="element" varStatus="loop">
<input id="input-#cc.attrs.customId-#loop.index" type="hidden"
value="#element.value" />
<div id="div-#cc.attrs.customId-#loop.index" class="list-box-row">#element.label</div>
</ui:repeat>
</h:panelGroup>
</composite:implementation>
但是这种解决方法不是很好,因为复合组件背后的想法是您可以通过许多不同的选项重用它们。硬编码 ajax 有点限制选项。
【讨论】:
【参考方案2】:我相信你的问题是这样的:
<f:ajax execute="newOnTherapy newExemption newDoctor newCareDate" ...
execute
属性可以引用特殊关键字(例如@this、@form 等...)或根据文档:
如果指定了文字,则它必须是由空格分隔的组件标识符和/或关键字之一的字符串。
当您将此组件作为子组件插入到父组件时,组件 ID 将由动态确定,不能绝对引用。
【讨论】:
这似乎不是问题。如果我使用@form,结果是一样的,至少根据萤火虫,正确的ID被发送回服务器。 @moneyt 虽然它仍然是错误的,但老实说,如果发送正确的 id 以供执行,我会感到难过。如果我是你,我会将 JSF 源代码附加到我的 IDE 库并开始浏览 Mojarra。这可能会给你更多的线索。 你确定是错的吗?在这种情况下,组件 ID 不是绝对引用的,而是相对的。我的理解是,如果 ID 上没有前导“:”,它就不是绝对的,JSF 会在当前表单中搜索匹配的 ID。我想如果它找不到 ID,我会收到错误消息,告诉我它无法解决它们。 @moneyt 我认为你是对的......我忘记了相关部分。以上是关于嵌套 JSF 复合组件中的 AJAX的主要内容,如果未能解决你的问题,请参考以下文章
JSF 2.0 复合组件 - ajax 渲染参数 OUTSIDE 组件定义
JSF 2 - 如何将 Ajax 侦听器方法添加到复合组件接口?
JSF 复合组件 <f:ajax> 包含未知 id - 无法在组件的上下文中找到它