jsf - ¿Cómo implemento un NamingContainer? Todos los niños obtienen la misma ID de cliente
tree custom-component (2)
Intento escribir mi propio componente de árbol. Un nodo de árbol se representa como un div que contiene componentes secundarios del componente de árbol, por ejemplo:
<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>
Hasta ahora, todo bien, todo funciona como se esperaba, pero el h:outputText
obtiene el mismo id varias veces.
Así que hice que mi componente implementara javax.faces.NamingController
, sobrescribiendo getContainerClientId()
:
@Override
public String getContainerClientId(FacesContext context) {
String clientId = super.getClientId(context);
String containerClientId = clientId + ":" + index;
return containerClientId;
}
index
se establece y actualiza durante la iteración sobre los nodos. Pero getContainerClientId()
se llama solo una vez para cada niño (no para cada iteración y cada niño, como era de esperar). Eso hace que cada identificador hijo tenga un prefijo con el mismo identificador de contenedor:
form:treeid:0:xxx
Lo mismo para sobrescribir getClientId()
.
¿Qué me perdí?
h: id de salida de texto te da lo mismo que no lo hiciste dinámico. Puedes crearlo como:
<h:outputText id="xxx_#{node.id}" value="#{node.name}" />
Asume que el nodo tiene un atributo ''id'' que es único.
La respuesta está oculta en la parte inferior del capítulo 3.1.6 de la especificación JSF 1.2 :
3.1.6 Identificadores de cliente
...
El valor devuelto por este método debe ser el mismo durante toda la vida de la instancia del componente a menos que se
setId()
asetId()
, en cuyo caso se volverá a calcular mediante la siguiente llamada agetClientId()
.
En otras palabras, el resultado de getClientId()
es guardado en caché por defecto por el componente JSF tal como se implementó en UIComponentBase#getClientId()
(vea también nullcheck en la línea 275 como en Mojarra 1.2_15 ) y este caché se restablece cuando el UIComponentBase#setId()
(consulte también la línea 358 tal como está en Mojarra 1.2_15 ). Siempre que no restablezca la ID del cliente en caché, devolverá el mismo valor en cada llamada getClientId()
.
Por lo tanto, al renderizar los elementos encodeChildren()
en la implementación de encodeChildren()
de su componente o del procesador, lo más probable es que se vea así,
for (UIComponent child : getChildren()) {
child.encodeAll(context);
}
debe hacer que cada hijo llame a UIComponent#setId()
con el resultado de UIComponent#getId()
para restablecer el ID del cliente almacenado en caché internamente antes de codificar el elemento secundario:
for (UIComponent child : getChildren()) {
child.setId(child.getId());
child.encodeAll(context);
}
La clase UIData
detrás de la implementación <h:dataTable>
hace por cierto también (vea la línea 1382 como está en Mojarra 1.2_15 ). Tenga en cuenta que esto no es específico de JSF 1.x, lo mismo se aplica a JSF 2.x también (y también en la clase UIRepeat
detrás de Facelets <ui:repeat>
).