23 de maio de 2011

JSF 2.0: Componentes PrimeFaces 2.2.1 - Parte 3

Vamos dar continuidade nas postagens sobre os componentes do PrimeFaces na versão 2.2.1. Na postagem anterior tratamos mais a parte de painéis, hoje será tratado de componentes um pouco mais úteis do que aqueles.

Estrutura do projeto
Caso não saiba como montar a estrutura do projeto veja aqui.
Depois de pronto o projeto fica com essa estrutura:

Antes de começar vamos ver o código fonte do nosso bean TesteBean.java que irá auxiliar os componentes (aos poucos vamos entendendo como essa classe será utilizada):
@Named
@SessionScoped
public class TesteBean implements Serializable {

    private int numeroSlider;
    private double valorSpinner;
    private List<String> animais;

    public void confirmar() {
        FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Confirmação", "Você confirmou alguma coisa!");
        FacesContext.getCurrentInstance().addMessage(null, message);
    }

    public int getNumeroSlider() {
        return numeroSlider;
    }

    public void setNumeroSlider(int numeroSlider) {
        this.numeroSlider = numeroSlider;
    }

    public double getValorSpinner() {
        return valorSpinner;
    }

    public void setValorSpinner(double valorSpinner) {
        this.valorSpinner = valorSpinner;
    }

    public List<String> getAnimais() {
        animais = new ArrayList<String>();
        animais.add("Girafa");
        animais.add("Pato");
        animais.add("Leopardo");
        animais.add("Elefante");
        animais.add("Zebra");
        return animais;
    }

    public void setAnimais(List<String> animais) {
        this.animais = animais;
    }

}

Começando...
Nossa tela irá ficar assim:


Bom... na imagem não aparecem todas os componentes, alguns deles serão chamados pelos botões "Confirmar" e "Abrir janela".

Agora vamos começar de uma vez, com um componente que é bem util, é o p:confirmDialog ele é um janelinha que geralmente é utilizada em casos de confirmação de exclusão ou confirmar alguma outra coisa:

Ao clicar no botão "Sim", a janela se fecha e mostra um mensagem a partir do componente p:messages (visto na primeira postagem), assim:


Código-fonte do componente:
<p:messages id="mensagens" showDetail="true" />
<p:commandButton value="Confirmar" onclick="confirmacao.show()" type="button"/>
<h:form>
    <p:confirmDialog widgetVar="confirmacao" header="Confirmar" message="Deseja confirmar alguma coisa?" severity="alert" modal="true">
        <p:commandButton value="Sim" update="mensagens" oncomplete="confirmacao.hide()" actionListener="#{testeBean.confirmar}" />
        <p:commandButton value="Não" onclick="confirmacao.hide()" type="button" />
    </p:confirmDialog>
</h:form>
Vamos entender o que está acontecendo neste código, inicialmente eu tenho um p:messages que será atualizado quando eu clicar no commandButton correspondente ao "Sim", esse commandButton chama o método confirmar que retorna uma mensagem ao JSF pelo FacesContext. A propriedade modal do confirmDialog serve pra bloquear que possa ser feito outras ações com este componente em funcionamento, ou seja ele faz tipo um painel de vidro, não deixando a possibilidade de mexer nos componentes atrás dele.

O segundo componente é bem parecido com o anterior, porém mais maleável, é o p:dialog, ele é praticamente um painel só que não é fixo na tela, ele realmente funciona como uma janelinha independente que abre, fecha e que pode ser movida de lugar:

Código-fonte do componente:
<p:commandButton value="Abrir janela" onclick="janela.show()" type="button" />
<p:dialog widgetVar="janela" header="Novo..." modal="true" width="500" height="230">
 ...
</p:dialog>
No código fonte completo (final da postagem) eu coloquei alguns outros componentes dentro deste dialog, apenas para mostrar que os componentes podem ser usados juntos (mas todos eles podem funcionar muito bem separadamente).

O componente p:wizard é algo até complicado de explicar para o que serve, pois depende da criatividade de quem for usar, mas o que eu posso dizer é que funciona quase como a tabView (vista na segunda postagem) mas ao invés de clicar na tab para abri-la, existem dois botões, um de avançar e outro de voltar uma tab:


Código-fonte do componente:
<p:wizard nextLabel="Avançar" backLabel="Voltar">
    <p:tab title="Slider">
        <p:panel header="Testando o slider">
        </p:panel>
    </p:tab>
    <p:tab title="Spinner">
        <p:panel header="Testando o spinner">
        </p:panel>
    </p:tab>
</p:wizard>
Veja que o mesmo é composto da mesma tag p:tab, já utilizada por outros componentes (na segunda postagem), quanto ao p:panel, não é obrigado ter ele alí, eu só coloquei para dar uma entendida melhor que se pode fazer com esse componente.

p:slider é um componente para números utilizado juntamente com um inputText (talvez com outros elementos também, não testei isso), o interessante dele é que pode-se definir intervalos numéricos:


Código-fonte do componente:
<h:outputText value="Numero:" />
<p:inputText id="numero1" value="#{testeBean.numeroSlider}"/>
<p:slider for="numero1" minValue="-100" maxValue="100" style="width: 127px"/>

Bom, como nem tudo é perfeito... a versão 2.2.1 do PrimeFaces também não é perfeita, e sim.. ela tem bugs, por exemplo o componente p:spinner não renderiza corretamente (veja no link a renderização correta dele), mas mesmo assim vou mostrar ele, é bom saber que ele existe, pode ser que em outra versão ele funcione perfeitamente:


Código-fonte do componente:
<h:outputText value="Valor:" />
<p:spinner value="#{testeBean.valorSpinner}" min="-10" max="10" stepFactor="0.10" showOn="hover"/>
Assim como o slider, este componente permite definir um intervalo numérico e também a quantidade de quanto ele deve somar ou subtrair do numero atual, isso é configurado na propriedade stepFactor. Para não ficar devendo: no Blog tem uma postagem sobre ajax nativo do JSF 2.0, onde o primeiro componente funciona meio parecido com este. Caso encontre uma solução para este bug eu volto aqui e atualizo esta postagem (se alguém souber essa solução, será bem vinda).

Agora sim vamos ver um componente bem útil, é a p:dataTable que é uma tabela que pode ser paginada e tudo mais, no link tem muitas formas de utilização dela, aconselho dar uma olhada, aqui vou mostrar o básico do básico dela:


Código-fonte do componente:
<p:dataTable value="#{testeBean.animais}" var="a" paginator="true" rows="4">
    <p:column headerText="Nome dos animais">
        <h:outputText value="#{a}" />
    </p:column>
    <p:column style="width: 30px">
        <p:commandButton image="ui-icon-wrench" />
    </p:column>
</p:dataTable>
Vou levar em consideração que você que está lendo já tenha conhecimento da h:dataTable do JSF puro, assim posso pular os detalhes... neste caso eu estou usando ela com paginação, então eu preciso dizer quantas linhas eu quero que apareça em cada página, para isso eu uso a propriedade rows.
Obs: é importante saber que ao utilizar um h:column em uma p:dataTable, esta não renderizará corretamente, por isso eu uso o p:column.

E pra finalizar, um componente que funciona bem parecido com o componente acima, porém é mais maleável também que é o p:dataGrid:


Veja que ele usa paginação da mesma forma que a p:dataTable, abaixo do código explicarei alguns detalhes.
Código-fonte do componente:
<p:dataGrid value="#{testeBean.animais}" var="a" paginator="true" columns="2" rows="4">
    <p:column>
        <p:panel header="Animal">
            <h:outputText value="Este animal é um #{a}" />
        </p:panel>
    </p:column>
</p:dataGrid>
Veja que eu estou usando a mesma lista que usei na p:dataTable, a diferença é que, na p:dataGrid é utilizada apenas um p:column, essa unica coluna funciona como um laço de repetição. Vamos aos detalhes importantes: a propriedade columns serve para definir quantas colunas devem ser mostradas, já a propriedade rows à primeira impressão ela engana, pois é, quando eu vi ela, pensava que era como a rows da p:dataTable que seria a quantidade de linhas que teria minha dataGrid, mas não é... na realidade ela corresponde a quantidade de elementos que devem ser mostrados na página: como eu defini que teria apenas duas colunas, por exemplo eu não posso querer que mostre apenas três elementos na dataGrid (ficaria meio furada) para isso não acontecer, a dataGrid preenche todos os lugares, não deixando apenas três elementos e um espaço em branco, as vezes mesmo eu explicando fica difícil de entender, por isso aconselho fazer testes com ela.

Enfim, o código fonte completo:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <p:messages id="mensagens" showDetail="true" />
        <p:commandButton value="Confirmar" onclick="confirmacao.show()" type="button"/>
        <h:form>
            <p:confirmDialog widgetVar="confirmacao" header="Confirmar" message="Deseja confirmar alguma coisa?" severity="alert" modal="true">
                <p:commandButton value="Sim" update="mensagens" oncomplete="confirmacao.hide()" actionListener="#{testeBean.confirmar}" />
                <p:commandButton value="Não" onclick="confirmacao.hide()" type="button" />
            </p:confirmDialog>
        </h:form>

        <p:separator />

        <p:commandButton value="Abrir janela" onclick="janela.show()" type="button" />
        <p:dialog widgetVar="janela" header="Novo..." modal="true" width="500" height="230">
            <h:form>
                <p:wizard nextLabel="Avançar" backLabel="Voltar">
                    <p:tab title="Slider">
                        <p:panel header="Testando o slider">
                            <h:outputText value="Numero:" /><br />
                            <p:inputText id="numero1" value="#{testeBean.numeroSlider}"/>
                            <p:slider for="numero1" minValue="-100" maxValue="100" style="width: 127px"/>
                        </p:panel>
                    </p:tab>
                    <p:tab title="Spinner">
                        <p:panel header="Testando o spinner">
                            <h:outputText value="Valor:" /><br />
                            <p:spinner value="#{testeBean.valorSpinner}" min="-10" max="10" stepFactor="0.10" showOn="hover"/>
                        </p:panel>
                    </p:tab>
                </p:wizard>
            </h:form>
        </p:dialog>

        <p:separator />

        <h:form>
            <p:dataTable value="#{testeBean.animais}" var="a" paginator="true" rows="4">
                <p:column headerText="Nome dos animais">
                    <h:outputText value="#{a}" />
                </p:column>
            </p:dataTable>
        </h:form>

        <p:separator />

        <h:form>
            <p:dataGrid value="#{testeBean.animais}" var="a" paginator="true" columns="2" rows="4">
                <p:column>
                    <p:panel header="Animal">
                        <h:outputText value="Este animal é um #{a}" />
                    </p:panel>
                </p:column>
            </p:dataGrid>
        </h:form>

    </h:body>
</html>

Acredito que por hoje chega... =)

18 comentários:

  1. Oi Andei lendo todos os post e pratiquei criando um projeto usando o primefaces, usei o elemento layout ficou muito bom, mas tentei usar o p:confirmDialog para mostrar se desejava realmente excluir ele congela junto. Por acaso sabe como resolver isso, procurei pela internet ninguem comentou sobre isso. Testei sem o elemento layout e funciona na boa.

    obrigado.

    ResponderExcluir
  2. Olá Silvio! Sempre usei o p:confirmDialog sem problemas nenhum com p:layout ... só tem um bug quando vc usa o modal do confirmDialog, mas não lembro em que versão exata isso acontece

    ResponderExcluir
  3. Oi!

    Descobri como resolver, tem que colocar ele fora do p:layout ai ele aparece normal(eu colocava o p:confirmDialog logo abaixo do botão dentro <p:layoutUnit position="center"... . Eu estou usando a versão prime 2.2.

    obs.: adorei seu blog, tá nos favoritos.

    Valeu Andii, obrigado.

    ResponderExcluir
  4. Olá! Gosto muito do seu Blog! Estou iniciando agora com JSF e PrimeFaces e estou tendo uma dificuldade enorme em resolver um problema com tabView! Qro clicar em um dataTable que está dentro de uma tab ou em um botão e conseguir abrir a próxima aba. Tentei fazer isso com o p:Wizard mas ele bloqueia a próxima aba, e eu gostaria q essa aba também ficasse habilitada!
    Já agradeço! Bjs
    PS: Mulheres programadoras YES!

    ResponderExcluir
  5. Olá Vanessa... depois de muito tempo venho te responder! rs ... bom, se eu não me engano a TabView deve ter alguma propriedade relacionada a activeIndex (algo com esse sentido), vc terá que controlar isso por um int em um bean, e quando clicar no botão, chamar um metodo que muda essa activeIndex e atualizar sua tabView ...

    ResponderExcluir
  6. Boa noite!

    Por acaso está em seus planos postar alguma aplicação rodando no GAE(Google App Engine)?

    Excelentes posts!

    ResponderExcluir
  7. Olá Luis... Obrigada! Assim que eu aprender sim :)

    ResponderExcluir
  8. Muito mas Muito útil pra mim...que Deus lhe recompense...

    ResponderExcluir
  9. Muito bom, mas tenho uma duvida, quando coloco a paginação, o dataTable me mostra (1 of 1) como faço para alterar o of ?

    ResponderExcluir
    Respostas
    1. Olá Diego, essa eu realmente não sei porque não vi nenhuma dataTable mostrando assim, mas geralmente o primefaces pega o idioma padrão do servidor.

      Excluir
    2. Oi andii, não sei se realmente pega pelo idioma do servidor pois acho que não, o meu ta configurado em portugues e mesmo assim fica como ingles, mas mesmo assim valeu pela resposta!

      Excluir
    3. Diego, agora que eu lembrei que isso existe... descobri que essa configuração está em primefaces.js (dentro da biblioteca), caso vc entenda bem de javascript... este é a configuração:
      PrimeFaces.widget.Paginator=function(b){
      this.cfg=b;
      this.jq=$();
      var a=this;
      $.each(this.cfg.id,function(c,d){
      a.jq=a.jq.add($(PrimeFaces.escapeClientId(d)))
      });
      this.pagesContainer=this.jq.children(".ui-paginator-pages");
      this.pageLinks=this.pagesContainer.children(".ui-paginator-page");
      this.rppSelect=this.jq.children(".ui-paginator-rpp-options");
      this.jtpSelect=this.jq.children(".ui-paginator-jtp-select");
      this.firstLink=this.jq.children(".ui-paginator-first");
      this.prevLink=this.jq.children(".ui-paginator-prev");
      this.nextLink=this.jq.children(".ui-paginator-next");
      this.endLink=this.jq.children(".ui-paginator-last");
      this.currentReport=this.jq.children(".ui-paginator-current");
      this.cfg.rows=this.cfg.rows==0?this.cfg.rowCount:this.cfg.rows;
      this.cfg.pageCount=Math.ceil(this.cfg.rowCount/this.cfg.rows)||1;
      this.cfg.pageLinks=this.cfg.pageLinks||10;
      this.cfg.currentPageTemplate=this.cfg.currentPageTemplate||"({currentPage} of {totalPages})";
      this.bindEvents()
      };

      Excluir
    4. Andii, entendi só que como faço para alterar somente o arquivo .js do primefaces? Sendo um .jar que utilizo no projeto, e o arquivo primefaces.js fica nas dependencias do meu projeto e não consigo alterar....

      Excluir
    5. Diego, vc teria que baixar o código fonte do PrimeFaces, e recompilar ele... mas não sei se o primefaces ainda disponibiliza esse código, se sim, não sei te dizer como encontrá-lo.
      Pois lembro que até a versão 2.2.1 tinha essa possibilidade.

      Excluir
  10. Boa noite, estou testando o primefaces, e em um dos testes tenho o botao de login, porem o mesmo só funciona no primeiro clique. Já viu algum erro parecido?

    Muito bacana seu blog!!

    ResponderExcluir
  11. Olá,
    Como faço para utilizar um Spinner dentro de uma lista? Nas tentativas que fiz, com uma lista de itens, implementando o spinner em cada um dos ítens da listagem, independente de, se eu clicar no spinner do primeiro item da lista ou no segundo ou terceiro, ele sempre pega o valor do último item da lista.

    ResponderExcluir

Deixe seu comentário... ;)