在 c:forEach 中渲染 h:panelGroup 时出现错误的 id

Posted

技术标签:

【中文标题】在 c:forEach 中渲染 h:panelGroup 时出现错误的 id【英文标题】:wrong ids when render h:panelGroup's inside c:forEach 【发布时间】:2013-05-18 20:00:56 【问题描述】:

我有一个页面,我在其中呈现一些 h:panelGroup 面板。这些面板被实现为在启动时在插件注册表中注册的插件。 插件 api 的一部分是一个自定义 jsf 组件,我在其中获取扩展点的注册插件并通过路径包含它们的 facelet 模板:

<c:forEach items="#pluginRegistry.getPlugins(point)" var="extension">
    <ui:include src="#extension.path" />
</c:forEach>

我包含面板的页面如下所示:

<h:panelGrid id="dashboard" columns="3">
    <cmf:insertPageFragments point="dashboardExtensionPoint" />
</h:panelGrid>

对于每个面板,都有如下 facelet 模板:

<rich:panel id="caseDetailsPanel" header="panel label">
    <!-- panel content -->
</rich:panel>

现在,问题是 pluginsRegistry 返回的列表中的第一个面板在页面中呈现,并使用提供的 id,例如 formId:caseDetailsPanel。其余的都生成了像 formId:j_idt223 这样的 id !!!显然,如果我想重新渲染一些面板,我不能这样做。

当环境是 jboss AS 7.1 和 JSF 2.1,richfaces 4.2.3.Final 时会发生这种情况。 当部署在 jboss-eap-6.1 上时,一切看起来都很好,但现在我不能使用这个 jboss 版本。

关于如何解决此问题的任何建议?

【问题讨论】:

【参考方案1】:

不能有多个具有相同 ID 的 JSF 组件。每个 JSF 组件都必须有一个唯一的 ID。使用 JSTL 动态创建 JSF 组件时,需要手动分配并确保唯一 ID,否则 JSF 会丢弃提供的 ID 并自动生成唯一 ID。

有几种方法可以实现这一点,具体取决于具体的功能要求和现有代码。

    使用&lt;c:forEach&gt;的迭代索引。

    <c:forEach ... varStatus="loop">
        ...
        <rich:panel id="caseDetailsPanel_#loop.index" ...>
    

    这将根据当前迭代索引生成caseDetailsPanel_0caseDetailsPanel_1等。

    使用当前迭代项的唯一标识符。根据目前提供的信息尚不清楚您是否有任何信息,因此这里只是一个虚构的示例,假设#extension 后面的类具有代表技术数据库标识符的id 属性。

    <c:forEach ... var="extension">
        ...
        <rich:panel id="caseDetailsPanel_#extension.id" ...>
    

    如果需要,将 #1 或 #2 包装在带有唯一标识符的 &lt;f:subview&gt; 中,这样您就无需修改包含。

    <c:forEach ... varStatus="loop">
        <f:subview id="panel_#loop.index">
            <ui:include ... />
    

    &lt;f:subview&gt; 在它周围创建一个新的NamingContainer,所以你最终会得到formId:panel_0:caseDetailsPanelformId:panel_1:caseDetailsPanel 等等。

一个完全不同的选择是使用&lt;ui:repeat&gt; 而不是&lt;c:forEach&gt;&lt;ui:repeat&gt; 不在视图构建期间运行,而是在视图渲染期间运行。这样,在组件树中实际上只有一个&lt;rich:panel id="caseDetailsPanel"&gt; 组件,它在生成html 期间被多次重用,由此JSF 将负责生成具有&lt;ui:repeat&gt; 索引的正确ID,例如formId:repeatId:0:caseDetailsPanel。但是,这反过来可能会导致 &lt;ui:include&gt; 出现问题,因为它也在视图构建期间运行,因此无法获得 #extension

【讨论】:

每个注册的面板都有自己的唯一ID。我作为代码示例发布的只是示例。 我相信您完全忽略了 JSTL 在 JSF 中的工作原理。我建议退后一步,仔细阅读***.com/a/3343681/157882,然后再次阅读我的答案。 @Adrian:这确实是相关的,但我不同意这是 JSTL/JSF 上的错误。另请参阅 Manfred 的评论 “c:forEach 标记不是 NamingContainer,因此它不应该做任何事情来确保是这种情况。” 您基本上是自己动态构建 JSF 组件树,所以您还应该自己处理唯一的 ID。当静态构建 JSF 组件树时,您也不会指定具有相同 ID 的多个组件,对吗?与 JSTL 方式的不同之处在于 JSF 不会因“Duplicate component ID”错误而崩溃,而是将其丢弃并自动生成一个。 所以,看起来我遇到的实际问题并不那么明显。 “显然,如果我想重新渲染一些面板,我不能这样做。”即使我将面板包装在命名容器中,由于某些操作,我也无法刷新特定面板。 大多数 JSF 启动器在“查看构建时间”与“查看渲染时间”以及哪些标签参与时确实失败了。不用担心,你不是唯一一个,你会明白的,答案已经详细给出了。编辑:根据您编辑的评论:您当然可以刷新它们。您可以在 render 属性中使用 EL。例如。 render="caseDetailsPanel_#loop.index"。这与命名容器完全无关。

以上是关于在 c:forEach 中渲染 h:panelGroup 时出现错误的 id的主要内容,如果未能解决你的问题,请参考以下文章

什么是Taglib?

c中foreach的用法

c:foreach语句在JSP页面获取不到值,页面只显示EL表达 请高手帮忙解答一下,谢谢。

如何在 JSP 页面的脚本标签中使用 <c:forEach>?

关于java中c标签foreach的用法?

关于java中c标签foreach的用法?