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.

18 Comments

  1. Erick Jeronimo disse:

    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.

  2. Erick Jeronimo disse:

    acho que o blog não aceita qualquer coisa similar a um html :P

    A tag é a a4j:KeepAlive.

  3. Rodrigo Bisterço disse:

    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.

  4. Ivan disse:

    Aonde baixo a aplicação JSF, RichFaces, AJAX e combobox alinhados.
    O link para Uirapuru.zip esta quebrado!
    Fique na paz!!!

  5. Ivan disse:

    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

  6. Ivan disse:

    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!

  7. Luciano disse:

    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.

  8. Sergio Fantin disse:

    Ae, o código tá funcionando muito bem. Certinho.
    Adaptei pro meu projeto. Agora é só alegria.

    Parabéns pelo post!

    Abraços…

  9. Sergio Fantin disse:

    Olá, Luciano.
    preciso renderizar um terceiro componente table assim que o primeiro combo for clicado, qual é a idéia?

    ———————————–
    Agradeço antecipadamente!

  10. Rafael Ponte disse:

    Muito bom!

    Um dos problemas mais comuns nas aplicações desenvolvidas com JSF e você explanou muito bem sobre o assunto!

    Parabéns!

  11. Tati disse:

    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.

    :)

  12. Savio disse:

    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

  13. Luciano disse:

    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.

  14. Luciano disse:

    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! =)

  15. Diego disse:

    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!

  16. Kleber Cardoso disse:

    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.

  17. Pollo disse:

    Do caralho!!!
    Por fin supe como resolver mi problema!!!

  18. Claudio Cesar disse:

    Muito obrigado, consegui fazer funcionar, logicamente tive que adaptar a minha realidade no código, mas deu certo. Parabéns pela didática.

Leave a Reply