如何实现NamingContainer?所有孩子都获得相同的客户ID

Zee*_*mee 4 tree jsf naming-containers custom-component jsf-1.2

我尝试编写自己的树组件.树节点呈现为包含树组件的子组件的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>
Run Code Online (Sandbox Code Playgroud)

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

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

index在迭代过程中设置和更新节点.但getContainerClientId()每个孩子只会被召唤一次(不是每次迭代和每个孩子,正如我所期望的那样).这会导致每个子ID都以相同的容器ID作为前缀:

form:treeid:0:xxx
Run Code Online (Sandbox Code Playgroud)

覆盖也是一样的getClientId().

我错过了什么?

Bal*_*usC 5

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

3.1.6客户端标识符

...

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

换句话说,结果getClientId()默认是由JSF组件缓存的UIComponentBase#getClientId()(在Mojarra 1.2_15中也见于第275行的nullcheck),并在UIComponentBase#setId()调用时重置此缓存(另请参阅第358行)在Mojarra 1.2_15).只要您不重置缓存的客户端ID,它将在每次getClientId()调用时返回相同的值.

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

for (UIComponent child : getChildren()) {
    child.encodeAll(context);
}
Run Code Online (Sandbox Code Playgroud)

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

for (UIComponent child : getChildren()) {
    child.setId(child.getId());
    child.encodeAll(context);
}
Run Code Online (Sandbox Code Playgroud)

UIData后面的类<h:dataTable>实现这是否顺便也(见行1382,因为它是在钻嘴鱼科1.2_15).请注意,这不是JSF 1.x特定的,同样适用于JSF 2.x(以及UIRepeatFacelets背后的类<ui:repeat>).