jsf tree custom-component jsf-1.2 naming-containers

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() a setId() , en cuyo caso se volverá a calcular mediante la siguiente llamada a getClientId() .

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> ).