5

我尝试编写自己的树组件。树节点呈现为包含树组件的子组件的 div,例如:

<my:tree id="extendedTree"
         value="#{controller.rootNode}"
         var="node">
    <h:outputText id="xxx" value="#{node.name}" />
    <h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
</my:tree>

到目前为止,一切都很好 - 一切都按预期工作,但h:outputText重复获得相同的 id。
所以我让我的组件实现javax.faces.NamingController,覆盖getContainerClientId()

@Override
public String getContainerClientId(FacesContext context) {
    String clientId = super.getClientId(context);
    String containerClientId = clientId + ":" + index;
    return containerClientId;
}

index在节点迭代期间设置和更新。但是getContainerClientId()每个孩子只调用一次(而不是每个迭代和每个孩子,如我所料)。这会导致每个子 id 都以相同的容器 id 为前缀:

form:treeid:0:xxx

覆盖也是一样getClientId()

我错过了什么?

4

3 回答 3

5

答案隐藏在JSF 1.2 规范第 3.1.6 章的底部:

3.1.6 客户端标识符

...

此方法返回的值在组件实例的整个生命周期内必须相同,除非setId()被调用,在这种情况下,它将在下一次调用时重新计算getClientId()

换句话说,getClientId()默认情况下,结果由 JSF 组件缓存UIComponentBase#getClientId()(参见第275 行的 nullcheck,因为它在 Mojarra 1.2_15 中),并且在调用时重置此缓存UIComponentBase#setId()(另请参见第 358 行,因为它在 Mojarra 1.2_15 中)。只要您不重置缓存的客户端 ID,它就会在每次getClientId()调用时返回相同的值。

因此,在encodeChildren()您的组件或渲染器的实现中渲染子组件时,它很可能看起来像这样,

for (UIComponent child : getChildren()) {
    child.encodeAll(context);
}

您应该为每个孩子调用UIComponent#setId(),结果是UIComponent#getId()在对孩子进行编码之前重置内部缓存的客户端 ID:

for (UIComponent child : getChildren()) {
    child.setId(child.getId());
    child.encodeAll(context);
}

UIData顺便说一句,实现背后的类也<h:dataTable>这样做(参见第 1382 行,因为它在 Mojarra 1.2_15 中)。请注意,这不是 JSF 1.x 特有的,同样适用于 JSF 2.x(以及UIRepeatFacelets 后面的类<ui:repeat>)。

于 2013-11-03T10:28:02.180 回答
0

值得一提的是,如果您的组件的子组件有子组件,那么可能还需要刷新它们缓存的 id。有了这个标记,稍微改编自原文:

<my:tree id="extendedTree"
         value="#{controller.rootNode}"
         var="node">
  <h:panelGroup layout="block" id="nodeBlock">
    <h:outputText id="xxx" value="#{node.name}" />
    <h:commandLink value="Test" actionListener="#{controller.nodeSelectionActionListener}" />
  </h:panelGroup>
</my:tree>

<panelGroup>在应用上面的 BalusC 修复后,id出来了,但所有子组件在迭代器中都出现了 0。

为了解决这个问题,也遍历所有级别的孩子并刷新他们缓存的 id。所以: child.setId(child.getId());变成函数定义uncacheId(child);的地方:uncacheId

private void uncacheId(UIComponent el) {
    el.setId(el.getId());
    el.getChildren().forEach(this::uncacheId);
}

这可能很明显,但我花了一段时间才弄清楚,所以......

于 2020-08-27T09:50:25.573 回答
-2

h:outputText id 为您提供与您没有使其动态相同的信息。您可以像这样创建它:

 <h:outputText id="xxx_#{node.id}" value="#{node.name}" />

假设节点具有唯一的“id”属性。

于 2013-10-28T13:31:35.270 回答