我正面临着一个邪恶的小问题,现在我已经躲过了近2天。我有一个p: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>
准备好捣碎的钻头。选择案例中的导航使用p:ajax。因为除非您在右侧页面上,否则具有id tickerEditForm的表单不可见,c:if控制包含ajax事件。添加新子节点时的导航使用h: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合作过的任何同事都无法弄清楚发生了什么。有人能看出问题所在吗?