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> getCiudades() { 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> getDistritos() { 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)
UPDATE:
Me preguntaron que cosa hace el uiUtils, el uiUtils es un seam bean stateless que tiene un método llamado getComboBox el cual devuelve un Map de labels, ids, la función hace un query a la bd que retorna un map con los dos valores.
Otra alternativa a usar un Map es utlilizar una lista de SelectItem, el objeto tiene el siguiente constructor en su modalidad más sencilla:
new SelectItem(Object valor, String label)
Por lo tanto simplemente es iterar sobre el resultado del query y crear los objetos y retornar la lista.
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="onchange" 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="onchange" 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.
Hola @Giancarlo, sabes he probado tu ejemplo pero la verdad no me queda muy claro me gustaria, saber si puedo llenar el combo con una lista de entitys, y tambien que esa entity que selecciono se guarde en el value del combo saludos.
lo que encontre es que no entiendo como empata tu combo con una lista de ciudades si en tu baking bean no esta ese atributo.
Casi me olvido darte las Gracias Giancarlo “Gracias ” lo logre con la variable q me recomendaste.
Hola Roberto,
Gracias por hacerme notar que que getCiudades y getDistritos estaban mal en el bean. Ya actualicé el post para que funcionen ok.
Respecto a tu duda de las entidades, Los combos no manejan entidades por default (creo que si hay componentes seam para hacerlo), sólo manejan SelectItems y Map donde String corresponde al label y Integer al value del combo.
En este caso nosotros hemos creado una función utilitaria en java que recibe el query y en base a eso retorna un Map indicandole además como parametro que columna va a funcionar como label y que columna como id.
Espero esto resuelva tus dudas
BUEN POST
Gracias @Giancarlo, ya lo logre haciendo un map, y aqui comporto la manera de hacerlo…
http://robertoleon.com.mx/manejo-de-combos-con-dos-o-mas-valores-en-richfaces/
por otro lado me gustaria ponerme en contacto contigo, mi correo es j.ro…@gmail.com saludos =)
Hola, Giancarlo Corzo
Porfavor podrias pasarme los archivos como ejemplo, ya que soy un novato o.O! y aun ando batallando con este pequeño detalle ya que no se si como lo utilizo con los managebean separados este mal su uso.
De ante mano Gracias!
Hola, David es muy interesante tu codigo ya que he estado haciendo pruebas con algo similar solo que tengo un detalle, no se a que libreria pertenece el uiUtiles por que soy un novato en Java, me gustaria poderlo replicar con Pais, Estado y Ciudad de ante Mano gracias
Hola Carlos,
el uiUtils es una librería que yo he implementado para abstraer la construcción de Maps en base al retorno de un query.
Haré un update para que se entienda.
Hola, Giancarlo Corzo
Disculpa que insita pero intentio implementarlo pero no logro hacerlo, si haces referencia a este estado o ando perdido
@Stateless
Un bean stateless es un bean que se solo vive mientras la invocación al método este activa.
Pero en este caso como accedo desde el bean puede ser una clase cualquiera con métodos static o algo así.
Si igual quieres hacerlo con un bean stateless para poder accederlo desde el xhtml.
Se declara así:
@Name(“uiUtils”)
@Scope(ScopeType.STATELESS)
public class uiUtils implements Serializable
Saludos.
Estoy haciendo el mismo ejemplo pero con tres combos, Paises, Ciudades y Estados; cuando selecciono el combo de paises, me aparece el de estado y pero cuando selecciono el de estados, el de ciudades no sufre cambio alguno se genera la llamada ajax pero el metodo del backing bean no se ejecuta, si tienes alguna sugerencia… y gracias
Hola Rart3001
Dado que los combos se actualizan de manera descendente, osea Pais luego Cuidad y finalmente estado, seleccionar el combo de estado primero debería tener un tratamiento especial.
Si te das cuenta todo se está haciendo en el evento onchange (osea cuando el valor cambia).
Para lograr hacer que los demás combos se actualicen tendrías que cambiar la lógica en que son actualizados.
Ejemplo Si elijo el combo estado, en el evento onchange este debe validar y actualizar los otros dos combos, si por el contrario eliges el combo ciudad, entonces los otros dos combos (paises, estado) debería ser actualizado.
Esto escapa un poco al ejemplo, pero espero entiendas la idea.
Gracias por tu respuesta, Aunque hice lo que me dijiste, hasta metí todos los componentes en un panel y hago el reRender de todo el pana en cada selección pero igual no me funciona, lo estraño es que cuando selecciono el país en la consola puedo ver el mensaje de log que le coloque al método de cargar los estados, pero cuando selecciono el estado no veo que se ejecute el método de cargar las ciudades, pero en la consola de ajax y en el status puedo ver que se ejecuta la llamada ajax pero no el método en el backing bean. Si tienes alguna otra sugerencia gracias.
como hacer un panel con un menu principal, y dentro de este panel otro panel que ira cambiando su contenido de acxuerdo a la opcion del menu que se seleccione, con jsf y richfaces
Mil gracias!!!!!