Combos relacionados(Estado-Cidade) com JBoss Seam + RichFaces

Bom dia,
Alguém saberia me dizer se existe uma forma de se montar combos relacionados que não fosse "na unha" utilizando o JBoss Seam junto com o RichFaces ?

Eu fiz umas tentativas mas não deu mto certo…
o código ficou o seguinte…

ComboAjax.xhtml

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:a="http://richfaces.org/a4j"
                xmlns:rich="http://richfaces.org/rich"
                template="layout/template.xhtml">
    
    <ui:define name="body">
        
        <h:messages styleClass="message"/>
        
        <h:form>
               
                <!-- PAIS -->
                <s:decorate id="SelecaoPais" template="layout/edit.xhtml">
                    <ui:define name="label">País</ui:define>
                    <h:selectOneMenu id="cb_pais" 
                                     value="#{comboPaisEstado.pais}" >
                        
                        <s:selectItems value="#{paisList.resultList}" 
                                       var="pais" 
                                       label="#{pais.nome}" 
                                       noSelectionLabel="Selecione o país"/>
                        
                        <s:convertEntity />                              
                        
                        <a:support event="onchange"     
                                   action="#{comboPaisEstado.onChangePais}"
                                   reRender="SelecaoPais" />									
                    </h:selectOneMenu>	
                </s:decorate>
                
                <!-- ESTADO -->
                <s:decorate id="SelecaoEstado" template="layout/edit.xhtml">
                    <ui:define name="label">Estado</ui:define>
                    <h:selectOneMenu id="cb_estado" 
                                     value="#{comboPaisEstado.estado}" >
                        
                        <s:selectItems value="#{comboPaisEstado.pais.estados}" 
                                       var="estado" 
                                       label="#{estado.nome}" 
                                       noSelectionLabel="Selecione o estado"/>
                        
                        <s:convertEntity />                              
                        
                    </h:selectOneMenu>	
                </s:decorate>
                      
        </h:form>
        
    </ui:define> 
</ui:composition>

Componente que trabalha o onChange

package session.beans;

import entity.beans.Estado;
import entity.beans.Pais;
import helper.JPAHelper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.jboss.seam.annotations.Name;

@Name("comboPaisEstado")
public class ComboPaisEstado {
    
    /** Creates a new instance of ComboPaisEstado */
    public ComboPaisEstado() {
    }
    
    /* adicionado */
    private Pais pais = new Pais();
    private Estado estado = null;
    
    public Pais getPais() {
        return pais;
    }
    
    public void setPais(Pais pais) {
        this.pais = pais;
    }

    public Estado getEstado() {
        return estado;
    }

    public void setEstado(Estado estado) {
        this.estado = estado;
    }
               
    public void onChangePais(){
        JPAHelper.initEMF();
        EntityManager em = JPAHelper.getEntityManager();
        Query query = em.createQuery( "select estado from Estado estado where estado.pais.id = :idPais" );
        query.setParameter( "idPais", pais.getId() );  
        
        Set<Estado> estados = new HashSet<Estado>();
        estados.addAll( (List<Estado>) query.getResultList() );
        pais.setEstados( estados );
    }
  
}

Pelo site de exemplos do RichFaces não existe nda parecido, assim como na documentação do Seam…
Não queria ter o mesmo trabalho pra fazer isso como nos tempos de Struts 1.2.X onde tinha que montar as funções JS de requisição e de callback…
Se puderem me indicar uma maneira mais tranquila de se fazer isso eu agradeço !

Boa Tarde pessoal,
como consegui resolver o problema passei aqui para deixar a solução, pode ser que seja útil para alguém mais pra frente…

A solução é bem simples, na tag a:support colocada dentro do primeiro combo definimos que no evento onchange o componente de id cb_estado terá de ser renderizado novamente. E como o selectItems do segundo combo é uma coleção que está contida dentro do objeto da primeira seleção, o mesmo é populado quendo selecionamos o primeiro objeto…

Tah ai o código:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:s="http://jboss.com/products/seam/taglib"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:a="http://richfaces.org/a4j"
                xmlns:rich="http://richfaces.org/rich" template="layout/template.xhtml">
    
    <ui:define name="body">
        
        <h:messages styleClass="message" />
        
        <h:form>
            
            <!-- PAIS -->
            <h:selectOneMenu id="cb_pais" value="#{comboPaisEstado.pais}">
                
                <s:selectItems value="#{paisList.resultList}" 
                               var="pais"
                               label="#{pais.nome}" 
                               noSelectionLabel="Selecione o país" />
                
                <s:convertEntity />
                
                <a:support event="onchange" [b]reRender="cb_estado"[/b] ignoreDupResponses="true" />
            </h:selectOneMenu>
            
            
            <!-- ESTADO -->
            <h:selectOneMenu [b]id="cb_estado"[/b] value="#{comboPaisEstado.estado}">
                
                <s:selectItems [b]value="#{comboPaisEstado.pais.estados}"[/b] 
                               var="estado"
                               label="#{estado.nome}" 
                               noSelectionLabel="Selecione o estado" />
                
                <s:convertEntity />
                
            </h:selectOneMenu>
            
            <a:status>
                <f:facet name="start">
                    <h:graphicImage value="/img/loading.gif"/>
                </f:facet>
            </a:status>            
            
        </h:form>
        
    </ui:define>
</ui:composition>

Eu criei uma solução para isso utilizado o auto completar do rich faces e um conversor personalizado do JSF. Para não sobrecarregar o servidor com consustas sql criei uma Factory do Seam para essas entidades.

Se alguem quiser dar uma olhada: http://www.chutaqueemacumba.com.br/ChutaQueEMacumba/site/cadastre.seam
Só digitar o nome da sua cidade que será autocompletado.

Se agradar alguem posso compartilhar.

Att.

Cara vcs podem mandar o código para essa solução? Estou passando por um probleminha… abraços

Devido a varias pessoas que tem me pedido vou preparar um tutorial junto aos fontes que desenvolvi e postarei aqui ok!

Abraços

e ae cara, pode ser uma boa, mas pode mandar esse pedacinho do código só para eu tentar… vou postar aqui meu código…
ainda esotu tentando ajeitar, não é o código final, algumas váriaveis vão sair…


package com.citespace.bean.list;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.faces.convert.Converter;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;


import com.citespace.bean.list.interfaces.ICountryList;
import com.citespace.cms.model.Country;
import com.citespace.converter.CountryConverter;

@Stateful
@Name("countryList")
@Scope(ScopeType.EVENT)
public class CountryList implements ICountryList, Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 4824589054075219364L;

	private List<Country> mCountry;
	
	private Map<String, Country> mCountryMap;
		
	private Country country = null;
	
	@PersistenceContext(unitName="citespace-jpa")
    private EntityManager em;

	@Create
	@SuppressWarnings("unchecked")
	public void loadCountry(){
		mCountry = 
			em.createQuery("select c from Country c ").getResultList();
		Map<String, Country> results = new TreeMap<String, Country>();
		for(Country c:mCountry){
			results.put(c.getName(), c);
		}
		
		mCountryMap = results;
	}
	
	public Map<String, Country> getAllCountries(){
		return mCountryMap;
	}
	
	
	public Converter getConverter(){
		return new CountryConverter(mCountry);
	}
	
	
	@Remove @Destroy
	public void destroy(){}

	
	public Country getCountry() {
		return country;
	}

	public void setCountry(Country country) {
		this.country = country;
	}
	
	

}
package com.citespace.bean.list;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.faces.convert.Converter;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

import com.citespace.bean.list.interfaces.IStateList;
import com.citespace.cms.model.Country;
import com.citespace.cms.model.State;
import com.citespace.converter.StateConverter;

@Stateful
@Name("stateList")
@Scope(ScopeType.EVENT)
public class StateList implements IStateList, Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -1901355293311882031L;

	private List<State> mState;
	
	private Map<String, State> mStateMap;
	
	private Country country = null;
	
    private State state = null;
    
	@PersistenceContext(unitName="citespace-jpa")
    private EntityManager em;

	@Create
	@SuppressWarnings("unchecked")
	public void loadState(){
		
		Query q = 
			em.createQuery("select s from State s ");		
		mState = q.getResultList();
		Map<String, State> results = new TreeMap<String, State>();
		for(State s:mState){
			results.put(s.getName(), s);
		}
		
		mStateMap = results;
	}
	
	public Map<String, State> getAllStates(){
		return mStateMap;
	}
	
	@SuppressWarnings("unchecked")
	public Map<String, State> getStatesByCountry(){
		Query q = em.createQuery("select s from State s Join s.country c Where c.id = :countryId");
		q.setParameter("countryId", country.getId());
		mState = q.getResultList();
		Map<String, State> results = new TreeMap<String, State>();
		for(State s:mState){
			results.put(s.getName(), s);
		}
		return mStateMap;
	}
	
	public Converter getConverter(){
		return new StateConverter(mState);
	}
	
	
	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}

	public Country getCountry() {
		return country;
	}

	public void setCountry(Country country) {
		this.country = country;
	}

	@Remove @Destroy
	public void destroy(){}	
}
<h:outputLabel value="Country * :" for="c" />
						<h:selectOneMenu value="#{stateList.country}" converter="#{countryList.converter}" required="true" id="srchCountry"  >																							
							<s:selectItems noSelectionLabel="-- Select a country --" />
							<s:selectItems  value="#{countryList.allCountries}" id="c" var="country" label="#{country.value}"/>
							<a4j:support event="onchange" reRender="s" ignoreDupResponses="true" /> 								
						</h:selectOneMenu>
					<h:outputLabel value="State * :" for="s" />
						<h:selectOneMenu value="#{stateList.state}" converter="#{stateList.converter}" required="true" id="srchState" >																							
							<s:selectItems noSelectionLabel="-- Select a state --" />
							<s:selectItems  value="#{stateList.statesByCountry}" id="s" var="state" label="#{state.value}"  /> 	
						</h:selectOneMenu>

só que não consigo funcionar o ajax para trazer de um determinado país… pode me dar uma dica, please?

to pegando esse erro

14:09:16,391 ERROR [viewhandler] Error Rendering View[/page/users.xhtml]
javax.faces.FacesException: javax.el.ELException: /page/users.xhtml @48,106 value="#{stateList.statesByCountry}": Error reading 'statesByCountry' on type org.javassist.tmp.java.lang.Object_$$_javassist_seam_3
	at javax.faces.component.UISelectItems.getValue(UISelectItems.java:144)
	at org.jboss.seam.ui.component.UISelectItems.getValue(UISelectItems.java:161)
	at com.sun.faces.renderkit.RenderKitUtils.getSelectItems(RenderKitUtils.java:289)
	at com.sun.faces.renderkit.html_basic.MenuRenderer.renderSelect(MenuRenderer.java:814)
	at com.sun.faces.renderkit.html_basic.MenuRenderer.encodeEnd(MenuRenderer.java:280)
	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:861)
	at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:242)
	at com.sun.faces.renderkit.html_basic.GridRenderer.renderRow(GridRenderer.java:180)
	at com.sun.faces.renderkit.html_basic.GridRenderer.encodeChildren(GridRenderer.java:127)
	at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:837)
	at org.jboss.seam.ui.util.cdk.RendererBase.renderChild(RendererBase.java:186)
	at org.jboss.seam.ui.util.cdk.RendererBase.renderChildren(RendererBase.java:166)
	at org.jboss.seam.ui.renderkit.ValidateAllRendererBase.doEncodeChildren(ValidateAllRendererBase.java:33)
	at org.jboss.seam.ui.util.cdk.RendererBase.encodeChildren(RendererBase.java:92)
	at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:837)
	at org.ajax4jsf.renderkit.RendererBase.renderChild(RendererBase.java:282)
	at org.ajax4jsf.renderkit.RendererBase.renderChildren(RendererBase.java:262)
	at org.richfaces.renderkit.html.PanelRenderer.doEncodeChildren(PanelRenderer.java:220)
	at org.richfaces.renderkit.html.PanelRenderer.doEncodeChildren(PanelRenderer.java:215)
	at org.ajax4jsf.renderkit.RendererBase.encodeChildren(RendererBase.java:121)