绑定多次包含的 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}。以此类推。