30 de março de 2011

JSF 2.0: Ajax nativo

Nessa postagem iremos fazer alguns exemplos de utilização da tag <f:ajax /> que é opção nativa para utlizar o ajax na versão 2.0 do JSF.
Bom, vou postando os códigos fonte de cada exemplo e explicando o que cada um faz, ao final posto o bean que utilizei para os testes.
Obs: todos os códigos dos componentes devem estar dentro de um h:form...

Primeiro componente:
Inicialmente eu vou criar um componente, onde eu tenho uma caixinha de texto, e dois botões, um para somar e outro para subtrair um determinado valor da caixinha de texto, assim:

Código:
<h:inputText id="txtNumero" value="#{testeBean.numero}" />
<h:commandButton value="+" actionListener="#{testeBean.somar}">
 <f:ajax render="txtNumero" execute="@this" />
</h:commandButton>
<h:commandButton value="-" actionListener="#{testeBean.subtrair}">
 <f:ajax render="txtNumero" execute="@this" />
</h:commandButton>

Veja que a tag ajax está entre os commandButtons, e usa a propriedade render para renderizar apenas a caixinha de texto, e o execute para executar apenas a ação do commandButton a qual pertence.

Segundo componente:
Este é um dos componentes mais úteis, que é um combo de estados, e quando seleciona o estado, carrega as cidades do estado selecionado.

Código:
<h:selectOneMenu valueChangeListener="#{testeBean.carregarCidades}">
 <f:selectItem itemLabel="Mato Grosso" itemValue="1" />
 <f:selectItem itemLabel="Paraná" itemValue="2" />
 <f:ajax event="change" render="cidades" />
</h:selectOneMenu>

<h:selectOneMenu id="cidades">
 <f:selectItems value="#{testeBean.cidades}" />
</h:selectOneMenu>

Neste código, eu coloquei os estados manualmente (nada impede de ter uma lista de selectItems), a tag f:ajax, utiliza do evento change, e renderiza o combo de cidades, o método valueChangeListener, serve para carregar as cidades conforme o estado selecionado, o codigo do bean pode ser feito completamente diferente, eu fiz dessa forma apenas para mostrar como funciona o evento change do f:ajax.

Terceiro componente:
Este componente é composto de uma caixinha de texto que atualiza um outputText, cada vez que uma letra é digitada.

Código:
<h:inputText value="#{testeBean.palavra}" >
 <f:ajax event="keyup" render="texto"/>
</h:inputText>
<h:outputText id="texto" value=" A palavra é: #{testeBean.palavra}" />

Aqui está mostrando a utilização do evento keyup da tag f:ajax, a cada letra digitada o f:ajax irá renderizar o outputText com o id texto.

Quarto e último componente:
Este é um dos mais simples, apenas mostra como funciona a tag f:ajax em um commandButton, ao clicar no commandButton, é chamado um método que trás a data e hora atual.

Código:
<h:commandButton value="Pegar data e hora" actionListener="#{testeBean.pegarDataHora}" >
 <f:ajax render="dataHora"/>
</h:commandButton>
<h:outputText value="A data e hora é: " />
<h:outputText value="#{testeBean.dataHora}" id="dataHora">
 <f:convertDateTime pattern="dd/MM/yyyy hh:MM:ss" />
</h:outputText>

Em um commandButton, o padrão do evento da tag f:ajax é o click, veja que por isso não especifiquei o event, apenas mando atualizar o meu outputText dataHora.

Agora segue o código do bean, como disse anteriormente, pode ser feito de outra forma, principalmente a parte de carregar as cidades, dessa forma ficou meio estranha, mas é que eu não quis usar o banco de dados nesse exemplo.

TesteBean.java
@Named
@SessionScoped
public class TesteBean implements Serializable {

    private int numero = 0;
    private List<String> cidades;
    private String palavra;
    private Date dataHora;

    public void somar() {
        numero = numero + 1;
    }

    public void subtrair() {
        numero = numero - 1;
    }

    public void carregarCidades(ValueChangeEvent evento) {
        String codigoEstado = evento.getNewValue().toString();
        cidades = new ArrayList<String>();

        if (codigoEstado.equals("1")) {
            cidades.add("Primavera do Leste");
            cidades.add("Cuiabá");
            cidades.add("Santo Antonio do Leste");
        } else if (codigoEstado.equals("2")) {
            cidades.add("Curitiba");
            cidades.add("Palmas");
        }
    }
    
    //gerar getters e setters
}

14 comentários:

  1. Valeu deu uma luz prum probleminha aqui! :)

    ResponderExcluir
  2. Boa noite!
    Quero dizer que eu e minha noiva somos gratos pelo seu trabalho. Já nos ajudou muitas vezes. Mas, não querendo abusar da sua boa vontade (rs), gostaria de tirar uma dúvida.
    É possível usar dois eventos ao mesmo tempo em um componente com ajax?
    Ex:
    Eu queria usar isso numa situação igual a que vc fez no segundo exemplo.

    Desde já agradeço!

    ResponderExcluir
  3. Olá Casper_RJ! Fico feliz que meu blog esteja ajudando vcs :)
    Pelo o que eu entendi da sua dúvida, seria a seguinte, por exemplo: dentro de um SelectOneMenu eu ter um ajax que execute quando eu clicar nele e outro que execute quando eu selecionar alguma coisa... se for isso, que eu me lembre, nunca testei algo desse jeito, mas acredito que deve funcionar sim, desde que vc coloque uma f:ajax para o evento do clique e um f:ajax para o evento do change. Tendo assim dentro do SelectOneMenu duas tags f:ajax... bom, pelo o que eu já mexi até hoje no JSF, espero não estar falando besteira hihihi

    ResponderExcluir
  4. Sei que estou dando um de "Coveiro", mas nunca é tarde para agradecer e elogiar, então obrigado e como sempre ótimo post.

    ResponderExcluir
  5. Ola tentei usar o componente 2 com acesso ao banco mas nao deu certo veja meu exemplo.












    Meu bean
    FabricanteDAO fabricanteDao;
    private List produtos;
    private List modelos;


    public List listarProduto(){
    fabricanteDao = new FabricanteDAO();
    produtos = new ArrayList();
    produtos = fabricanteDao.getProdutos();
    return produtos;
    }

    public void listarModelo(ValueChangeEvent evento) {
    String prodselecionado = evento.getNewValue().toString();
    fabricanteDao = new FabricanteDAO();
    modelos = new ArrayList();
    modelos = fabricanteDao.getModelos(prodselecionado);

    }

    e por ultimo a logicaDAO
    public List getProdutos() {

    List produtos = new ArrayList();

    ResultSet rs;
    try {

    rs = stmt.executeQuery("SELECT produto FROM test.produtos ORDER BY produto");

    while (rs.next()) {

    produtos.add(rs.getString("produto"));
    }

    } catch (SQLException ex) {
    Logger.getLogger(FabricanteDAO.class.getName()).log(Level.SEVERE, null, ex);
    }


    return produtos;

    }

    public List getModelos(String prod) {

    List modelos = new ArrayList();

    ResultSet rs;
    try {

    rs = stmt.executeQuery("SELECT modelo FROM test.produtos where produto = " + prod + " ");

    while (rs.next()) {

    modelos.add(rs.getString("modelo"));
    }

    } catch (SQLException ex) {
    Logger.getLogger(FabricanteDAO.class.getName()).log(Level.SEVERE, null, ex);
    }


    return modelos;

    }

    Voce dizer o que estou errando?
    Agradeço pelo post.
    ate+

    ResponderExcluir
    Respostas
    1. Ayres, vc confirmou se o prodselecionado não está chegando como null?
      aparece algum erro no console quando vc executa isso?

      Excluir
  6. Show de bola o post! Uma duvida, pq no selectOneMenu vc teve que usar um valueChangeListener? Sem ele não funcionaria apenas com o ajax change?

    ResponderExcluir
    Respostas
    1. Olá Lucas! o valueChangeListener está chamando meu método que carrega as cidades de acordo com o estado selecionado, nesse meu exemplo não funcionaria sem ele.

      Excluir
  7. Parabéns pela clareza nos exemplos.
    José Antônio

    ResponderExcluir
  8. Eu já vi vários posts que dão esse mesmo exemplo que os seus, mas por algum motivo o Ajax não está funcionando na minha aplicação, sendo que quando dou F12 dá o seguinte erro no Chome:
    Uncaught ReferenceError: jsf is not defined.

    Você conhece esse erro ?

    ResponderExcluir

Deixe seu comentário... ;)