selectOneMenu dependente em JSF

Senhores,

preciso criar dois selectOneMenu, sendo que um é dependente de outro. Por exemplo, um selectOneMenu em que é selecionado ESTADO, e dinamicamente, o outro selectOneMenu de MUNICIPIO é carregado.

Pois bem, consigo fazer com que ambos sejam sincronizados, logo, a alteração em um implica na alteração do outro. Contudo, tenho problemas no momento da submissão em que me deparo com erros no último selectOneMenu.

Alguém tem algum código pronto, ou sugestão de como contornar este problema?

qual o problema que acontece quando tu altera o segundo?

Dá uma olhada na tag <t:saveState/> do MyFaces Tomahawk.
Ele salva o estados dos objetos, tenta colocas 2 tags que salvam os estados das 2 collections q populam a suas combos.
Eu estava com exatamente o mesmo cenario e o mesmo problema, essa tag resolveu meus problemas =)

Este é o erro!

WARNING: executePhase(PROCESS_VALIDATIONS 3,com.sun.faces.context.FacesContextImpl@cc0e01) threw exception
javax.faces.FacesException
	at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:108 )
	at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:248 )
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at sso.DominoLoginFilter.doFilter(DominoLoginFilter.java:84)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:228 )
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128 )
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:212)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:818 )
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:624)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:445)
	at java.lang.Thread.run(Unknown Source)
Caused by: java.util.NoSuchElementException
	at javax.faces.component.SelectItemsIterator.next(SelectItemsIterator.java:117)
	at javax.faces.component.SelectItemsIterator.next(SelectItemsIterator.java:141)
	at javax.faces.component.SelectItemsIterator.next(SelectItemsIterator.java:49)
	at javax.faces.component.UISelectOne.matchValue(UISelectOne.java:165)
	at javax.faces.component.UISelectOne.validateValue(UISelectOne.java:137)
	at javax.faces.component.UIInput.validate(UIInput.java:868 )
	at javax.faces.component.UIInput.executeValidate(UIInput.java:1071)
	at javax.faces.component.UIInput.processValidators(UIInput.java:663)
	at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1021)
	at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1021)
	at javax.faces.component.UIForm.processValidators(UIForm.java:229)
	at javax.faces.component.UIComponentBase.processValidators(UIComponentBase.java:1021)
	at javax.faces.component.UIViewRoot.processValidators(UIViewRoot.java:662)
	at com.sun.faces.lifecycle.ProcessValidationsPhase.execute(ProcessValidationsPhase.java:100)

Agora veja os códigos que uso, primeiro o JSF:

<h:outputText 
value="Unidade Administrativa"/>

<h:selectOneMenu 
id="unidade" 
immediate="true" 
required="true" 
value="#{DitecConteqCadastrar.unidade}" 
valueChangeListener="#{DitecConteqCadastrar.selecionaUnidade}" 
onchange="submit()">

<f:selectItems 
value="#{DitecConteqCadastrar.unidades}"/>

</h:selectOneMenu>
				
<h:outputText 
value="Seção/Divisão"/>

<h:selectOneMenu 
id="divisao" 
required="true" 
value="#{DitecConteqCadastrar.divisao}">

<f:selectItems 
value="#{DitecConteqCadastrar.divisoes}"/>

</h:selectOneMenu>

<h:commandButton 
value="Cadastrar" 
action="#{DitecConteqCadastrar.cadastrarComputador}"/>

Agora o backing bean:

public class Cadastrar{
	private String unidade;
	private String divisao;
	
	public Cadastrar(){}
	
	public SelectItem[] getUnidades(){
		BDConteq conteq = new BDConteq();
		Hashtable hashtable = conteq.getUnidades();
		Enumeration enumeration = hashtable.keys();
		SelectItem[] unidades = new SelectItem[hashtable.size()];
		int i = 0;
					
		while(enumeration.hasMoreElements()){
			String codigo = (String) enumeration.nextElement();
			String nome = (String) hashtable.get(codigo);				
			unidades[i] = new SelectItem(nome, codigo);
			i++;
		}
			
		return unidades;			
	}
	
	public SelectItem[] getDivisoes(){
		if (unidade != null){
			BDConteq conteq = new BDConteq();
			Hashtable hashtable = conteq.getDivisoes(unidade);
			Enumeration enumeration = hashtable.keys();
			SelectItem[] divisoes = new SelectItem[hashtable.size()];
			int i = 0;
				
			while(enumeration.hasMoreElements()){
				String sigla = (String) enumeration.nextElement();
				String nome = (String) hashtable.get(sigla);
				divisoes[i] = new SelectItem(nome, sigla);
				i++;
			}
				
			return divisoes;		
		}
		return new SelectItem[0];
	}
	
	public String getUnidade(){return unidade;}
	public String getDivisao(){return divisao;}
	public void setUnidade(String unidade){this.unidade = unidade;}
	public void setDivisao(String divisao){this.divisao = divisao;}
	
	public void selecionaUnidade(ValueChangeEvent event){
		if (event.getNewValue() != null){
			unidade = (String) event.getNewValue();
			FacesContext.getCurrentInstance().renderResponse();
		}
	}
	
	public String cadastrarComputador(){
		System.out.println(unidade);
		System.out.println(divisao);
	}
}

cara, como o André falou, a solução para o teu problema pode ser o uso da tag t:saveState. Só para não duplicar a explicação do porque isso deve estar acontecendo, dá uma olhada nesse post http://www.guj.com.br/posts/list/48462.java
Espero que ajude.
falow.

Olá,

Só para constar nos autos, é possível usar o atributo actionListener do primeiro selectOneMenu, sem nenhum componente ou controle adicional na jsp para tanto.

Basta um método no seu bean (void e que recebe como argumento um ValueChangeEvent) para filtrar o segundo selectOneMenu de acordo com a opção selecionada no primeiro.

Schmidt

Agradeço a ajuda de todos, mas o uso dessa tag só ocorrerá em último caso, porque aqui no trabalho temos um ambiente fechado muito burocrático, e atualmente não temos muita flexibilidade na escolha das ferramentas a serem usadas.

Enfim, é algo estranho que no momento que eu clico no botão de Submit ocorre o estranho erro que já mandei em anexo. Não existe nenhuma forma de fazer isso usando apenas JSF?

Usando o meu post anterior! :shock:

Flws!

caralho, esse selectOneMenu tá dando muito stress, até aqui

por exemplo, o meu funciona adicionando uma list associada ao f:selectItems

o foda é que em outra página o código tá IGUALZINHO e funciona certinho, mas quando a página é renderizada, joga uma excessão (na outra página)

sério, minha paciencia tá quase esgotando aqui…

Bom, se nao tem como usar outras coisas então complica. Pela minha esperiência eu indico você dar uma conversada com o pessoal da tua empresa e considerar fortemente o uso daquela tag (t:saveState).
Apesar deu nao ter olhado certinho como você está atualizando o segundo combo :oops: , pelo erro já imagino que seja aquilo que eu comentei no post que mandei o link. Pelo fato do JSF facilitar algumas coisas, a gente acaba esquecendo que por baixo dos panos é tudo request e response, e então a gente estressa mesmo. A solução seria jogar tudo na session :shock: , ou então usar esse componente que é bem inteligente e resolve esse problema.
Para não usar nenhuma dessas alternativas realmente você vai ter dorzinha de cabeça :twisted:
bom, essa é apenas a MINHA opinião, baseada na esperiência que eu tenho.

Wow!

Relaxem, se esse for o problema de vocês, fiquem contentes!  :wink: 
Como já foi comentado, existem algumas formas diferentes de fazer essa implementação. 

Gilliard citou uma que utiliza um componente do tomahawk. Não sei como funciona pois nunca usei, mas deve resolver com certeza. Temos um detalhe da dependência que isso agrega, conforme comentado pelo criador deste tópico.

 Uma segunda forma de resolver, usando apenas faces, é a que eu sugerí. Para tanto, usa-se normalmente os dois selectOneMenu. Estes selects encapsulam sim, f:selectItems que apontam pra listas do seu bean. O primeiro selectOneMenu terá 3 particularidades: os atributos "onchange", valueChangeListener (peço desculpas pelo post que fiz antes, havia dito actionListener, está errado) e por fim, o "immediate".

 Esse valueChangeListener é o mais importante, integra a renderização faces com a sua lógica de negócios. Ou seja, o MethodBinding usado nesse atributo precisa ser capaz de buscar a sua lista de Municípios de acordo com o Estado selecionado. O "onchange" é "requisito não funcional", precisa ser usado para que a jsp seja renderizada novamente com os valores das listas atualizadas. E pra terminar, o immediante precisa ser igual a "true", pois é possível que vc tenha outros campos na sua jsp e vc não deseja validá-los (ou preenchê-los caso sejam obrigatórios) para capturar Estados/Municípios.

Se ainda restarem dúvidas, mandem uma MP.

Abraços,
Schmidt

ufa finalmente consegui resolver rsrs! quase explodi por causa do stress haha! :-o :-o

esse método submit() é javascript?

Se vc colocar o seu ManagedBean com o escopo = “session” vai funcionar, mas tem q ver se isso não trará problemas pra vc.

Sim, infelizmente. =)

 Pode-se usar onchange="this.form.submit();" mesmo.

 Quem usa algum framework auxiliar como Ajax, pode resolver isso de outras formas.

Schmidt

Bom, só justificando a minha resposta… é que eu tenho o costume de deixar para os gets resolverem os valores, pois uso ajax e assim, no meu caso, da forma como uso, acredito que fica mais fácil. Mas com certeza da pra fazer de outro jeito. E sobre usar ou não o tomahawk, a dependencia dele é muito pequena (voce pode usar com RI, e é esse o meu caso). Uma das vantagens do JSF é esse “mundaréu” de coisas/componentes que você encontra na net, então podar isso é deixar de usar muita coisa. Mas como é algo da sua organização eu não me meto, foi só um comentário.
Como pra tudo tem várias formas de fazer, escolha uma e bom trabalho :wink:

Só complementando… pra essa situação, eu uso a tag saveState e populo as combox com ajax usando Ajax4JSF.
Mto bom viu, rápido, simples e sem refresh na página :smiley:
Se conseguir implementar o Ajajax4JSF vai te poupar um teabalho tbm :slight_smile:

Concordo com Gilliard,

  Cada caso é diferente, projetos com requisitos ou necessidades específicas.

  Eu fiz um mega comentário sobre como fazer o que na prática se traduz em poucas linhas, simples mesmo. Entretanto, usar uma biblioteca com a do tomahawk é muito interessante, as funcionalidades suportadas são muitas e as vezes, em situações delicadas são quase "obrigatórias".

  Eu por exemplo brigo até hoje com dateTables do faces quando preciso renderizar galerias em colunas e linhas. Aí apelo!. =)

flws!

Gostaria de saber como fazer isso, pode me ajudar?

Gostaria de saber como fazer isso, pode me ajudar?

O esquema é simples… vc faz dois array’s. Um com os estados e outro com as UF’s.Ai tu joga num for e manda os ifs pra comparar…
depois tu cria outra function pra chamar isso tudo aew…
vlw