La carga dinámica de un combo basado en la selección de un combo previo es un problema relativamente clásico y que ha sido abordado de distintas maneras a lo largo del tiempo.
En este post queremos mostrarles como es que nosotros abordamos el tema usando componentes JSF y de Richfaces para resolver el problema.
Para este ejemplo usaremos 2 entidades (Ciudad y Distrito) donde una ciudad tiene múltiples distritos asociados y para el caso práctico asumiremos que ambas solo tienen un id y un nombre.
@Entity @Table(name = "CIUDAD") public class Ciudad implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID_CIUDAD", nullable = false) private Integer idCiudad; @Length(max=45) @Column(name = "NOMBRE",length=45) private String nombre; } @Entity @Table(name = "DISTRITO") public class Distrito implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "ID_CIUDAD", nullable = false) private Integer idDistrito; @Length(max=45) @Column(name = "NOMBRE",length=45) private String nombre; }
Además tendremos un managed bean de JSF llamado testBean que nos servirá para almacenar la información temporal de la página.
@Name("testBean") @Scope(ScopeType.CONVERSATION) public class TestBean implements Serializable { private Integer idCiudad = -1; //Omitimos sus gets y sets private Integer idDistrito = -1;//Omitimos sus gets y sets public Map<String,Integer> getCiudadList() { String query = "select new map(nombre as nombre, idCiudad as idCiudad) " + "from Ciudad order by nombre"; return uiUtils.getComboBox(query, "nombre", "idCiudad"); } public Map<String,Integer> getDistritoList() { String query = "select new map(d.nombre as nombre, d.idDistrito as idDistrito) " + "from Distrito d where d.ciudad.idCiudad = " + idCiudad + " order by nombre"; return uiUtils.getComboBox(query, "nombre", "idDistrito"); } }
Solo para simplificar el asunto el método getComboBox hace una consulta a la Base de datos y genera una estructura Map<String,Integer>
El signature del método es:
public Map<String,Integer> getComboBox(String query, String value, String key)
El código que corresponde a la parte de JSF se los presento a continuación:
<h:selectOneMenu value="#{testBean.idCiudad}" style="width:200px;"> <f:selectItem itemLabel="#{messages['comun.selectOneMenu.seleccione']}" itemValue="-1" /> <f:selectItems value="#{testBean.cuidades}"/> <a4j:support event="onclick" reRender="distritosPanel"/> </h:selectOneMenu> <a4j:outputPanel id="distritosPanel"> <ui:fragment rendered="#{testBean.idCiudad ne -1}"> <h:selectOneMenu value="#{testBean.idDistrito}" style="width:200px;"> <f:selectItem itemLabel="#{messages['comun.selectOneMenu.seleccione']}" itemValue="-1" /> <f:selectItems value="#{testBean.distritos}"/> </h:selectOneMenu> </ui:fragment> </a4j:outputPanel>
Como pueden ver es muy sencillo, cada combo tiene dos propiedades importantes, la primera es la que almacenará el resultado de la selección en este caso idCiudad y idDistrito, la segunda es la lista de opciones del combo el cual se obtiene de una propiedad List<SelectItem>, esta lista es generada en el managed bean haciendo consultas a la base de datos.
Los componentes JSF por default no tienen soporte AJAX, lo que significa que tendrían que hacer un submit cada vez que quisieran recargar una sección de la página, es por esto que utilizamos el tag a4j:support que nos permite darle una naturaleza AJAX al componente JSF.
<a4j:support event="onclick" reRender="distritosPanel"/>Como pueden ver al tag a4j:support tiene 2 propiedades, la primera es el evento que dispara la acción, este podría ser onclick, onmouseover, onmouseout, en este caso utilizamos onchange.La segunda propiedad es el parámetro reRender que le dice al componente las secciones de código que deben ser refrescadas una vez que el request AJAX haya terminado.
De esta manera podemos conseguir cargar dinámicamente un combo JSF mediante AJAX.
Más información
No related posts.

Muy buen blog.
Hace un tiempo postee un tema parecido éste : http://4cuatros.blogspot.com/2009/01/cargar-combos-con-adf-faces.html
muy poco explicativo…. la verdad sin el codigo del backbean no se entiende la logica del bean al que llama tu a4j… salu2
oye una preguntita sabes como cargar un page fragment desde codigo java sin utilizar ajax estoy utilizando el framewok visual web jsf de netbeans , gracias
recibo sugerencias sobre como cargar un div dinamicamente desde la capa java en con el plugin visual web java server faces sin utilizar ajax , en serio lo necesito , puedo quedar sin trabajo
… gracias .
David,
Bueno antes de Ajax las cosas se hacian con submits estándares, por lo tanto podrias hacer un submit modificar la información que ocultaba al fragment y luego hacer render de la página otra vez.
Lo que el Richfaces hace cuando hace un submit AJAX es regenerar el arbol de componentes en base a la nueva información del modelo, es por esto que se puede hacer reRender de una sección de la página, si quieres simular este comportamiento sin AJAX estricturamente por JAVA tendrías que de alguna manera modificar el árbol de componentes.
De hecho es más facil hacerlo de la forma en que dije al principio, mediante submits.
Giancarlo gracias por tu sugerencia , pues te comento lo que estoy haciendo aunque se que no es la manera mas adecuada ;pues agrege el div dentro de un panel layout (tengo tres layout y dentro de estos un div distinto); y lo que hago es con codigo java volver invisible los paneles q necesite segun el requerimiento de la pagina , Giancarlo crees que queda muy mal ? pues en cada div tengo un menu distinto y si se muestran dos menus al mismo tiempo seria un error imperdonable por que cada menu es para un tipo de usuario. gracias por todo . y espero pronto aprender a utilizar ajax.
Nicolas,
Respondiendo a tu pedido he extendido el post para que incluya las entidades utilizadas y el pojo que soportará las transacciones.
David,
Pero porque no manejas una variable USUARIO en conversation (que tenga información sobre el tipo de usuario con el que estas trabajando) y luego cuando haces render de la página evaluas esta variable y según eso haces render de la sección apropiada.