我正面临一个棘手的小问题,这已经困扰我近 2 天了。我得到了 ap:treeTable 组件,它用作导航和编辑分层数据结构的方法。树显示在左侧。通过选择树条目,用户选择要编辑的条目,然后将其显示在右侧的表单中。条目上还有一个用于添加新子节点的按钮。单击此按钮将创建所选节点的新子节点,右侧的表单将显示新节点。
树被实现为自定义标签。它有一个 mode 属性,它允许我通过提交一个名为“mode”的绑定来重新使用它进行简单的导航或编辑。这是树组件的摘录:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
...
xmlns:ticker="http://java.sun.com/jsf/composite/components/ticker/">
<h:body>
<composite:interface>
<composite:attribute name="mode" required="true" />
</composite:interface>
<composite:implementation>
<h:form id="tickeruebersicht">
<p:treeTable id="treetable"
binding="#{treeNavigationController.treeTable}"
value="#{treeNavigationController.root}"
var="node" selectionMode="'single'}"
selection="#{treeNavigationController.selection}">
...
<c:if test="#{cc.attrs.mode == 'editor'}">
<p:ajax event="select"
listener="#{tickerUI.onNodeSelect}"
update=":tickerEditForm"/>
</c:if>
...
<p:column style="width:5%" rendered="#{cc.attrs.mode == 'editor'}">
<h:commandLink title="new child node"
action="#{tickerUI.createNewChildNode}"
styleClass="ui-icon ui-icon-plus"
style="display:inline-block;">
<f:param name="parentNodeType" value="#{node.class.name}"/>
<f:param name="parentNodeID" value="#{node.id}"/>
</h:commandLink>
</p:column>
...
</p:treeTable>
</h:form>
</composite:implementation>
</h:body>
</html>
重要的部分都准备好了。选择案例中的导航使用 ap:ajax 工作。因为除非您在正确的页面上,否则 ID 为 tickerEditForm 的表单是不可见的,因此 c:if 控制 ajax 事件的包含。添加新子节点时的导航使用 ah:commandLink 代替。
以这种方式控制的表单如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<ui:composition template="template/master_layout.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
...
xmlns:ticker="http://java.sun.com/jsf/composite/components/ticker">
<ui:define name="leftcolumn">
<h:panelGroup layout="block" id="leftColumn">
<ticker:tickertree mode="editor" />
</h:panelGroup>
</ui:define>
...
<ui:define name="content">
<h:form id="tickerEditForm" acceptcharset="UTF-8">
<ui:fragment rendered="#{tickerUI.ticker != null}">
...
<span class="message">
<h:messages globalOnly="true" />
</span>
<ui:fragment rendered="#{tickerUI.neuerTicker}">
...
</ui:fragment>
<p class="inputLeft">
<h:outputLabel for="name">
<span>Name</span>
</h:outputLabel>
#{' '}
<h:inputText id="name" requiredMessage="Must enter name"
value="#{tickerUI.ticker.label}" required="true">
</h:inputText>
</p>
...
<ui:fragment id="zusatzdaten">
<fieldset class="clear">
<h:panelGroup rendered="#{tickerUI.editComponentName != null}">
<ui:include src="#{tickerUI.editComponentName}" />
</h:panelGroup>
</fieldset>
</ui:fragment>
<div class="controls">
<span class="save">
<h:commandButton
action="#{tickerUI.saveAction}" value="speichern" />
</span>
<span class="cancel">
<h:commandButton
action="#{tickerUI.createNewChildNode}" value="abbrechen" />
</span>
<span class="delete">
<h:commandButton
action="#{tickerUI.deleteAction}" value="löschen"
disabled="#{not empty tickerUI.ticker.descendants or tickerUI.neuerTicker}" />
</span>
</div>
</ui:fragment>
</h:form>
</ui:define>
<ui:debug />
</ui:composition>
最大的问题是,当使用 h:commandLink 导航到表单时,点击表单上的“保存”按钮会按预期调用操作方法。如果我使用 p:ajax 导航到表单,则表单的支持 bean 是新构建的,当然数据丢失并且不调用保存操作。两个支持 bean(tickerUI 和 treeNavigationController)都是 @ViewScoped。我试着去@SessionScoped 看看是否有区别,但没有区别。在 Tomcat 6 上使用 Mojarra 2.2.3 和 Primefaces 3.5。
我和这里与 JSF 一起工作的任何同事都无法弄清楚发生了什么。有人能看出问题所在吗?