JSF, RichFaces, AJAX e combobox alinhados
Em meus estudos sobre JSF e AJAX, gostei muito do pacote disponibilizado pela JBoss, o RichFaces. A intensão é usar o mais puro do JSF porém, aproveitando o que cada componente oferece de melhor.
O RichFaces é interessante e muito completo, oferece diversos recursos para web que até então o HTML estava limitado, como o combobox (leia-se também dropdown) com área aberta para localização de textos – <rich:comboBox/>. Ao contrário deste alguns outros componentes apresentam problemas sérios de incompatibilidade com o Apache MyFaces (Tomahawk), por exemplo, o <rich:fileUpload/>.
Estudando esses componentes observei suas vantagens e falhas, decidi então criar este exemplo de um recurso tão comum em formulários e simples com JSF, que são os combobox dependentes ou alinhados.
Vamos continuar com o esquema de Países e Estados, um combobox para cada, sendo que ao modificar o Pais através do AJAX os estados relacionados serão carregados.
A configuração básica do Faces e RichFaces no web.xml será ocultada, porém você poderá fazer o download do projeto no fim deste post.
O projeto está estruturado da seguinte maneira:
No Manager Bean destacam-se os métodos getPaises() e actionCarregarEstados(), o primeiro retorna um array de itens – SelectItem[] – tipo de dado complexo que será tratado pelo componente JSF, já o actionCarregarEstados() é responsável por carregar os estados relacionados ao pais.
public class UirapuruManagerBean {
private Pessoa pessoa = new Pessoa();
private SelectItem[] estados;
/* getters e setters ocultados */
/* carrega os paises */
public SelectItem[] getPaises(){
List<pais> lp = regiaoDao.getAllPais();
List<selectItem> itens = new ArrayList<selectItem>(lp.size());
for( Pais p : lp ){
itens.add( new SelectItem(p.getId_pais(), p.getNm_pais()) );
}// for end
return itens.toArray( new SelectItem[itens.size()] );
}
/* carrega os estados */
public SelectItem[] getEstadosByPais(int id_pais){
List<estado> estados = regiaoDao.getEstadosDoPais(id_pais);
List<selectItem> itens = new ArrayList<selectItem>(estados.size());
for( Estado e : estados ){
itens.add( new SelectItem(e.getId_estado(), e.getNm_estado()) );
}// for end
return itens.toArray( new SelectItem[itens.size()] );
}
/* */
public String actionCarregarEstados(){
this.estados = getEstadosByPais(getPessoa().getPais().getId_pais());
return "SUCCESS";
}
}
Uma observação importante antes de seguirmos: os beans Pessoa, Pais e Estado são simples e foram criados apenas para uma interpretação melhor, é importante e somente assim este exemplo irá funcionar perfeitamente se no bean Pessoa, os atributos pais e estado sejam inicializados. Já que não estamos trabalhando com Spring o Faces não irá fazer este entendimento e uma exceção NullPointerException será lançada, para evitar tal faça isto:
public class Pessoa implements Serializable {
private int id;
private String nome;
// criando os objetos
private Pais pais = new Pais();
private Estado estado = new Estado();
/* getters e setters ocultados */
}
Okay. Feita a observação vamos para a parte final, a montagem do JSP.
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core"%> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html"%> <%@ taglib prefix="a4j" uri="http://richfaces.org/a4j"%>
Não esqueça que qualquer página JSF deve ser elaborada dentro da tag <f:view/> e, que sem a definição do formulário <h:form/> o AJAX não irá funcionar.
<f:view>
<h:form>
<h:panelGrid border="0" columns="2" width="500" cellpadding="1" cellspacing="2">
<h:outputLabel value="Pais" for="pais"/>
<h:selectOneMenu id="pais"
value="#{umb.pessoa.pais.id_pais}"
rendered="true">
<f:selectItems value="#{umb.paises}"/>
<a4j:support event="onchange"
ajaxSingle="true"
action="#{umb.actionCarregarEstados}"
reRender="pais,estado"/>
</h:selectOneMenu>
<h:outputLabel value="Estado" for="estado"/>
<h:selectOneMenu id="estado"
value="#{umb.pessoa.estado.id_estado}"
rendered="true">
<f:selectItems value="#{umb.estados}"/>
</h:selectOneMenu>
</h:panelGrid>
</h:form>
</f:view>
Na tag <h:selectOneMenu/> deve-se colocar na propriedade VALUE o atributo que irá receber o valor selecionado no combobox, outras propriedades excenciais são o ID que identifica o componente e RENDERED que indica a renderização. A tag <f:selectItems/> invoca o método – umb.getPaises() – que contém os itens do combobox.
Até o momento utilizamos apenas JSF puro, o RichFaces é representado na tag <a4j:support/> responsável por identificar quando um evento do tipo OnChange é feito e fazer a ação definida no método umb.actionCarregarEstados, e por fim na propriedade reRender estão separados por vírgulas os componentes que serão renderizados.
Seguindo esta linha de raciocínio você conseguirá fazer vários níveis de dependência facilmente, o componente <rich:combobox/> também permite este tipo de interação, com alguns recursos a mais.
Faça aqui o download do projeto.










Fale cara, tudo bom?!
Achei bem interessante a tua abordagem sobre os combos dependentes, ficou show! :D
Eu segui esse teu tutorial em um sistema que eu to implementando, só que eu tive um problema na hora de armazenar o objeto no banco, tú enfrentou algo do tipo?
Tentei usar a tag pra resolver isso (meu managed bean tem escopo “request”).
Abraços.
acho que o blog não aceita qualquer coisa similar a um html :P
A tag é a a4j:KeepAlive.
Luciano,
Muito legal seu exemplo!! Parabéns.
Acontece que no zip do projeto, faltou um jar referenciado na sua TOMCATLIB, chamado UtilTools.jar.
Você poderia me envia-lo?
Agradeço desde já.
Um grande abraço.
Aonde baixo a aplicação JSF, RichFaces, AJAX e combobox alinhados.
O link para Uirapuru.zip esta quebrado!
Fique na paz!!!
Não estou conseguindo baixar os jars do rich faces, entrei no site do JBOSS baixo o rich faces, só que não encontro os jars, tem várias pastas compactadas só que não encontro os jars em menhuma delas.
Alguém pode me ajudar?
ivan_fit@yahoo.com.br
Estou tentando rodar a aplicação e importa tools.util.ConnManager e tools.util.Util. Acho que é o mesmo do post do Rodrigo.
Além do mais eu também gostaria de armazenar em banco.
A paz!
Pessoal, desculpe pelos links quebrados, foi por conta de uma mudança de servidor.
Abaixo o link para o UtilTool.zip.
http://www.lucianosilva.com/download/UtilTools.zip
Abraços.
Ae, o código tá funcionando muito bem. Certinho.
Adaptei pro meu projeto. Agora é só alegria.
Parabéns pelo post!
Abraços…
Olá, Luciano.
preciso renderizar um terceiro componente table assim que o primeiro combo for clicado, qual é a idéia?
———————————–
Agradeço antecipadamente!
Muito bom!
Um dos problemas mais comuns nas aplicações desenvolvidas com JSF e você explanou muito bem sobre o assunto!
Parabéns!
Oi Luciano!
Consegui fazer o exemplo funcionar, mas na maioria das situações precisamos passar o objeto todo (umb.pessoa.selectedPais) para o MB e não somente o seu ID (umb.pessoa.pais.id_pais). Acredito que esse seja o maior problema em se trabalhar com combos.
Se puder postar um exemplo desse tipo ajudaria muita gente. Tá dada a dica.
:)
Muito bom, mas to precisando criar componentes dinamicamente, tipo quando clicar num link aparece um inputtext, sera que vc naum teria um tutorial que faca isso.
Obrigado e parabens
Savio, você necessita disso em JSF mesmo?
Não sei qual o cenário que você está trabalhando, mas talvez utilizando um simples JavaScript resolva seu problema.
Se quiser posso tentar ajudar, mande em PVT para (luciano@lucianosilva.com).
Abraço.
Tati, talvez eu não tenha compreendido, mas se você utilizar a classe e não o atributo, irá dar uma exception (exception javax.servlet.ServletException: Value is no String) já que o espera uma String, entendeu?
Em que situação você irá precisar além do ID?
Valeu! =)
Luciano, estou tendo que fazer uma aplicação que utiliza exatamente este exemplo que você mostrou. Porém ao submeter a tela está apresentando a exceção java.util.NoSuchElementException. Quando seleciono uma combo, a segunda é preenchida corretamente, porém ao visualizar o código fonte, a segunda combo está vazia, como se não houvesse nenhum dado na lista.
Estou precisando muito disso!
Olá Luciano, td bem??
Gostaria de parabeniza-lo pelo artigo. Achei muito interessante. Gostaria de tirar uma dúvida com vc no que se refere à geração de banco. Estou tentando gerar o banco através de uma classe Gerabanco usando hibernate, porém está dando uma exception. Teria como vc me ajudar?? Agradeço man.
Do caralho!!!
Por fin supe como resolver mi problema!!!
Muito obrigado, consegui fazer funcionar, logicamente tive que adaptar a minha realidade no código, mas deu certo. Parabéns pela didática.