绑定多次包含的 p:dataTable 会导致 OF 唯一性异常
binding on p:dataTable which is included multiple times causes ID unqiueness exceptions
我正在尝试使用一个特殊的 PrimeFaces 数据导出器实现,正如在这个答案中所使用的那样:
然而,我们的用例有点复杂,例如使用 Facelets 显示相应的数据table 用于导出,其中包含带有子 table 的行扩展。
insiderListManagerDialogs.xhtml:
<ui:include src="insiderListManagerListDialog.xhtml">
<ui:param name="idPrefix" value="il-emp" />
<ui:param name="widgetVarPrefix" value="ilEmp" />
<ui:param name="dialogTitle" value="#{msg['entity.employee.internal.plural.label']}" />
<ui:param name="mapPropertyName" value="insiderListEmployeesMap" />
<ui:param name="sortColumn" value="#{ent.lastTime}" />
<ui:param name="keyColumnsXhtml" value="listDialogInsiderListKeyColumns.xhtml" />
<ui:param name="valueColumnsXhtml" value="listDialogEmployeeValueColumns.xhtml" />
</ui:include>
insiderListManagerListDialog.xhtml:
<p:dialog id="#{idPrefix}-il-view-dialog"
widgetVar="#{widgetVarPrefix}Dialog"
header="#{dialogTitle}"
modal="true"
resizable="false"
appendToBody="true"
onShow="resetFilterInput( '#{idPrefix}' );"
width="1000"
height="750">
<h:form id="#{idPrefix}-il-view-form">
<p:dataTable id="list-entities"
widgetVar="listEntitiesTable"
binding="#{table}"
value="#{insiderListManager.listInstances}"
var="key"
scrollable="true"
scrollHeight="625"
scrollWidth="985"
resizableColumns="true"
expandedRow="#{insiderListManager.listEntitiesRowTogglerOpen}"
filteredValue="#{insiderListManager.filteredEntities}"
sortBy="#{sortColumn}"
styleClass="bx-datatable-header-hidden-m"
tyle="width: 970px;">
<f:facet name="header">
<p:inputText id="globalFilter"
onkeypress="if (event.keyCode == 13) { listEntitiesTable.filter(); return false;}"
size="70"
styleClass="bx-dt-header-element" />
<p:commandButton id="search-btn"
icon="ui-icon bx-icon-search"
value="#{msg['common.action.search.label']}"
process="@this"
onclick="listEntitiesTable.filter(); return false;"
styleClass="bx-dt-header-element" />
<p:commandButton title="#{msg['common.action.excelExport.all.hint']}"
icon="ui-icon bx-icon-excel"
action="#{insiderListManager.exportXls( table )}"
ajax="false"
styleClass="bx-button-border bx-dt-header-element" />
<div class="bx-clear-float" />
</f:facet>
<p:column width="15"
resizable="false">
...
<p:rowExpansion>
<p:dataTable value="#{insiderListManager[mapPropertyName].get(key)}"
var="value">
<ui:include src="#{valueColumnsXhtml}" />
</p:dataTable>
</p:rowExpansion>
</p:dataTable>
<h:panelGroup id="button-panel" layout="block" styleClass="left-button-panel mt-5px">
<p:commandButton id="toggle-button"
icon="#{insiderListManager.listEntitiesRowTogglerOpen ? 'ui-icon ui-icon-circle-triangle-e' : 'ui-icon ui-icon-circle-triangle-s'}"
value="#{insiderListManager.listEntitiesRowTogglerOpen ? msg['common.action.collapseAll.label'] : msg['common.action.expandAll.label']}"
process="@this"
update="@form">
<f:setPropertyActionListener target="#{insiderListManager.listEntitiesRowTogglerOpen}"
value="#{not insiderListManager.listEntitiesRowTogglerOpen}" />
</p:commandButton>
</h:panelGroup>
</h:form>
</p:dialog>
注意
<p:commandButton title="..."
icon="ui-icon bx-icon-excel"
action="#{insiderListManager.exportXls( table )}"
... />
它正在使用绑定数据 table,如顶部发布的 link 中所述。
我们正在使用 JSF 2.1(Mojarra 2.1.22 - 我们无法切换到 MyFaces),它允许表达式从 [=16] 构建像 #{idPrefix}-il-view-dialog
这样的 ID =].
但是,我们遇到了 ID 唯一性异常:
Caused by: java.lang.IllegalStateException: Component ID il-emp-il-view-form:list-entities:globalFilter has already been found in the view.
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:846)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.application.view.StateManagementStrategyImpl.saveView(StateManagementStrategyImpl.java:144)
at com.sun.faces.application.StateManagerImpl.saveView(StateManagerImpl.java:133)
at com.sun.faces.application.view.WriteBehindStateWriter.flushToWriter(WriteBehindStateWriter.java:225)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:419)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
at com.ocpsoft.pretty.faces.application.PrettyViewHandler.renderView(PrettyViewHandler.java:163)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
... 49 more
如果省略,其他具有明确 ID 的按钮也会发生同样的情况,例如搜索按钮 (<p:commandButton id="search-btn" ...
).
问题:
为什么会发生这种情况?当我们必须为显示的每个用例使用 PrimeFaces 对话框时,我们如何避免这个问题? (我们有不止一个对话)
请注意,删除 <p:dataTable ... binding="..."
页面后可以正常工作。
您基本上是将物理上不同的 <p:dataTable>
组件绑定到同一个 EL 变量。如果此 EL 变量不为空(即已由同一视图中的先前组件设置),则 JSF 将 重用 它而不是创建一个新变量,并在期间覆盖其所有属性查看构建时间,导致视图渲染期间的 "weird" 行为和状态保存期间潜在的重复组件 ID 错误。
在您的特定情况下,您可以使用 UIComponent#getNamingContainer()
从子组件内部直接引用父 <p:dataTable>
组件。
<p:dataTable ... (NO binding!)>
...
<p:comandButton ... action="#{bean.exportXls(component.namingContainer)}" />
可能它嵌套在另一个 NamingContainer
组件中,那么您需要改用#{component.namingContainer.parent.namingContainer}
。以此类推。
我正在尝试使用一个特殊的 PrimeFaces 数据导出器实现,正如在这个答案中所使用的那样:
然而,我们的用例有点复杂,例如使用 Facelets 显示相应的数据table 用于导出,其中包含带有子 table 的行扩展。
insiderListManagerDialogs.xhtml:
<ui:include src="insiderListManagerListDialog.xhtml">
<ui:param name="idPrefix" value="il-emp" />
<ui:param name="widgetVarPrefix" value="ilEmp" />
<ui:param name="dialogTitle" value="#{msg['entity.employee.internal.plural.label']}" />
<ui:param name="mapPropertyName" value="insiderListEmployeesMap" />
<ui:param name="sortColumn" value="#{ent.lastTime}" />
<ui:param name="keyColumnsXhtml" value="listDialogInsiderListKeyColumns.xhtml" />
<ui:param name="valueColumnsXhtml" value="listDialogEmployeeValueColumns.xhtml" />
</ui:include>
insiderListManagerListDialog.xhtml:
<p:dialog id="#{idPrefix}-il-view-dialog"
widgetVar="#{widgetVarPrefix}Dialog"
header="#{dialogTitle}"
modal="true"
resizable="false"
appendToBody="true"
onShow="resetFilterInput( '#{idPrefix}' );"
width="1000"
height="750">
<h:form id="#{idPrefix}-il-view-form">
<p:dataTable id="list-entities"
widgetVar="listEntitiesTable"
binding="#{table}"
value="#{insiderListManager.listInstances}"
var="key"
scrollable="true"
scrollHeight="625"
scrollWidth="985"
resizableColumns="true"
expandedRow="#{insiderListManager.listEntitiesRowTogglerOpen}"
filteredValue="#{insiderListManager.filteredEntities}"
sortBy="#{sortColumn}"
styleClass="bx-datatable-header-hidden-m"
tyle="width: 970px;">
<f:facet name="header">
<p:inputText id="globalFilter"
onkeypress="if (event.keyCode == 13) { listEntitiesTable.filter(); return false;}"
size="70"
styleClass="bx-dt-header-element" />
<p:commandButton id="search-btn"
icon="ui-icon bx-icon-search"
value="#{msg['common.action.search.label']}"
process="@this"
onclick="listEntitiesTable.filter(); return false;"
styleClass="bx-dt-header-element" />
<p:commandButton title="#{msg['common.action.excelExport.all.hint']}"
icon="ui-icon bx-icon-excel"
action="#{insiderListManager.exportXls( table )}"
ajax="false"
styleClass="bx-button-border bx-dt-header-element" />
<div class="bx-clear-float" />
</f:facet>
<p:column width="15"
resizable="false">
...
<p:rowExpansion>
<p:dataTable value="#{insiderListManager[mapPropertyName].get(key)}"
var="value">
<ui:include src="#{valueColumnsXhtml}" />
</p:dataTable>
</p:rowExpansion>
</p:dataTable>
<h:panelGroup id="button-panel" layout="block" styleClass="left-button-panel mt-5px">
<p:commandButton id="toggle-button"
icon="#{insiderListManager.listEntitiesRowTogglerOpen ? 'ui-icon ui-icon-circle-triangle-e' : 'ui-icon ui-icon-circle-triangle-s'}"
value="#{insiderListManager.listEntitiesRowTogglerOpen ? msg['common.action.collapseAll.label'] : msg['common.action.expandAll.label']}"
process="@this"
update="@form">
<f:setPropertyActionListener target="#{insiderListManager.listEntitiesRowTogglerOpen}"
value="#{not insiderListManager.listEntitiesRowTogglerOpen}" />
</p:commandButton>
</h:panelGroup>
</h:form>
</p:dialog>
注意
<p:commandButton title="..."
icon="ui-icon bx-icon-excel"
action="#{insiderListManager.exportXls( table )}"
... />
它正在使用绑定数据 table,如顶部发布的 link 中所述。
我们正在使用 JSF 2.1(Mojarra 2.1.22 - 我们无法切换到 MyFaces),它允许表达式从 [=16] 构建像 #{idPrefix}-il-view-dialog
这样的 ID =].
但是,我们遇到了 ID 唯一性异常:
Caused by: java.lang.IllegalStateException: Component ID il-emp-il-view-form:list-entities:globalFilter has already been found in the view.
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:846)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.util.Util.checkIdUniqueness(Util.java:830)
at com.sun.faces.application.view.StateManagementStrategyImpl.saveView(StateManagementStrategyImpl.java:144)
at com.sun.faces.application.StateManagerImpl.saveView(StateManagerImpl.java:133)
at com.sun.faces.application.view.WriteBehindStateWriter.flushToWriter(WriteBehindStateWriter.java:225)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:419)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
at com.ocpsoft.pretty.faces.application.PrettyViewHandler.renderView(PrettyViewHandler.java:163)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:288)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:121)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594)
... 49 more
如果省略,其他具有明确 ID 的按钮也会发生同样的情况,例如搜索按钮 (<p:commandButton id="search-btn" ...
).
问题:
为什么会发生这种情况?当我们必须为显示的每个用例使用 PrimeFaces 对话框时,我们如何避免这个问题? (我们有不止一个对话)
请注意,删除 <p:dataTable ... binding="..."
页面后可以正常工作。
您基本上是将物理上不同的 <p:dataTable>
组件绑定到同一个 EL 变量。如果此 EL 变量不为空(即已由同一视图中的先前组件设置),则 JSF 将 重用 它而不是创建一个新变量,并在期间覆盖其所有属性查看构建时间,导致视图渲染期间的 "weird" 行为和状态保存期间潜在的重复组件 ID 错误。
在您的特定情况下,您可以使用 UIComponent#getNamingContainer()
从子组件内部直接引用父 <p:dataTable>
组件。
<p:dataTable ... (NO binding!)>
...
<p:comandButton ... action="#{bean.exportXls(component.namingContainer)}" />
可能它嵌套在另一个 NamingContainer
组件中,那么您需要改用#{component.namingContainer.parent.namingContainer}
。以此类推。