quinta-feira, 3 de outubro de 2013

Atualizando DataTables dinamicamente (III)

Este post faz parte de uma série que demonstra o uso de Primefaces, Spring, JPA, JSF para criar uma aplicação que atualiza as DataTables dos clientes automaticamente. Confira os outros posts da série aqui: Primefaces

No post anterior, criamos uma página que exibe os dados de uma entidade Autor em um componente p:dataTable do Primefaces. Vamos modificar a página criada para que ofereça as opções restantes de CRUD, ou seja, cadastrar, atualizar e excluir.

Para melhorar a aparência da aplicação, vamos criar ícones para representar algumas ações. As quatro imagens abaixo serão utilizadas para editar, excluir, gravar e adicionar.


Basta baixar as imagens (ou arranjar outras - fica à escolha de cada pessoa) e colocá-las em uma pasta do projeto chamada resources - se a pasta não existir, obviamente tem que ser criada. Eu costumo separar as imagens em uma subpasta chamada imagens, dentro de resources. Para facilitar as referências, vamos renomear as imagens para editar.png, excluir.png, gravar.png e adicionar.png, respectivamente.

Com as imagens já gravadas na pasta, vamos modificar a página autores.xhtml dessa maneira:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:p="http://primefaces.org/ui"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Livraria</title>
    </h:head>
    <h:body>
        <style>
            .panelGridSemBorda tr, .panelGridSemBorda td {
                border: none;
            }
            .dialogoSemScroll div.ui-dialog-content {
                overflow: hidden;
            }
        </style>
        <h:form prependId="false" id="frmPrincipal">
            <p:outputPanel id="pnlTabela">                
                <p:dataTable id="Tabela" 
                             widgetVar="wTabela"
                             rowIndexVar="rowNumber"
                             rows="10"
                             paginator="true"
                             value="#{controlador.autores}"
                             var="registro">
                    <f:facet name="header">
                        <h:outputText value="Autores"/>
                        <p:toolbar>
                            <p:toolbarGroup>
                                <p:commandLink action="#{controlador.adicionar('AUTOR')}"
                                               oncomplete="wdlgDetalhe.show()" 
                                               update=":frmPrincipal:pnlDetalhe">
                                    <p:graphicImage url="./resources/imagens/adicionar.png" 
                                                    style="background-color: transparent; vertical-align: middle; border: none" 
                                                    width="40" height="40"/>
                                </p:commandLink>
                            </p:toolbarGroup>
                        </p:toolbar>
                    </f:facet>
                    <p:column headerText="Nome">
                        #{registro.nome}
                    </p:column>
                    <p:column headerText="Sobrenome">
                        #{registro.sobrenome}
                    </p:column>
                    <p:column width="60">
                        <p:commandLink action="#{controlador.editar(registro)}" 
                                       oncomplete="wdlgDetalhe.show()" 
                                       update=":frmPrincipal:pnlDetalhe">
                            <p:graphicImage url="./resources/imagens/editar.png" 
                                            style="background-color: transparent; vertical-align: middle; border: none" 
                                            width="20" height="20"/>
                        </p:commandLink>
                        <p:spacer width="10"/>
                        <p:commandLink action="#{controlador.excluir(registro)}" 
                                       update=":frmPrincipal:pnlTabela">
                            <p:graphicImage url="./resources/imagens/excluir.png" 
                                            style="background-color: transparent; vertical-align: middle; border: none" 
                                            width="20" height="20"/>
                        </p:commandLink>
                    </p:column>
                </p:dataTable>
            </p:outputPanel>
            <p:dialog id="dlgDetalhe" 
                      widgetVar="wdlgDetalhe" 
                      modal="true" 
                      resizable="false" 
                      closable="true" 
                      header="Detalhes do autor" 
                      styleClass="dialogoSemScroll" >
                <p:panelGrid id="pnlDetalhe" 
                             columns="1" 
                             styleClass="panelGridSemBorda">
                    <p:outputLabel value="Nome:" id="lNome" for="cNome" />
                    <p:inputText id="cNome" value="#{controlador.autor.nome}"/>
                    <p:outputLabel value="Sobrenome:" id="lSobrenome" for="cSobrenome" />
                    <p:inputText id="cSobrenome" value="#{controlador.autor.sobrenome}"/>
                    <f:facet name="footer">
                        <p:toolbar>
                            <p:toolbarGroup>
                                <p:commandLink action="#{controlador.gravar(controlador.autor)}"
                                               oncomplete="wdlgDetalhe.hide()" 
                                               update=":frmPrincipal:pnlTabela">
                                    <p:graphicImage url="./resources/imagens/gravar.png" 
                                                    style="background-color: transparent; vertical-align: middle; border: none" 
                                                    width="40" height="40"/>
                                </p:commandLink>
                            </p:toolbarGroup>
                        </p:toolbar>
                    </f:facet>
                </p:panelGrid>
            </p:dialog>
        </h:form>
    </h:body>
</html>

O managed bean controlador ganhará novos atributos e métodos, como mostrado abaixo:

package controle;

import delegate.FacadeBD;
import entidades.Entidade;
import entidades.Autor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;

@ManagedBean(name = "controlador")
@ViewScoped
public class Controlador implements Serializable {

    @ManagedProperty(value = "#{facadeBD}")
    private FacadeBD facadeBD;
    private Autor autor = new Autor();
    private List<Autor> autores = new ArrayList<Autor>();

    public Controlador() {
    }

    public void adicionar(String entidade) {
        if (entidade.toUpperCase().equals("AUTOR")) {
            autor = new Autor();
        }
    }

    public void editar(Entidade entidade) {
        autor = (Autor) facadeBD.carregar(entidade.getClass(), entidade.getId());
    }

    public void excluir(Entidade entidade) {
        entidade.setFlagRemover(true);
        facadeBD.salvar(entidade);
        autores.clear();
    }

    public void gravar(Entidade entidade) {
        facadeBD.salvar(entidade);
        autores.clear();
    }

    public FacadeBD getFacadeBD() {
        return facadeBD;
    }

    public void setFacadeBD(FacadeBD facadeBD) {
        this.facadeBD = facadeBD;
    }

    public Autor getAutor() {
        return autor;
    }

    public void setAutor(Autor autor) {
        this.autor = autor;
    }

    public List<Autor> getAutores() {
        if (autores.isEmpty()) {
            autores = facadeBD.listar(Autor.class);
        }
        return autores;
    }

    public void setAutores(List<Autor> autores) {
        this.autores = autores;
    }
}

Vamos às linhas mais relevantes do arquivo xhtml, sem entrar no assunto "ciclo de vida do JSF":

  • No cabeçalho da tabela (entre as linhas 28 e 41), inserimos um commandButton que chama o método adicionar do controlador, e no final da requisição, atualiza o componente identificado por :frmPrincipal:pnlDetalhe e executa o método show do componente identificado por wdlgDetalhe.
  • O mesmo acontece entre as linhas 49 e 55, sendo que o método chamado é o editar do controlador.
  • Na linha 57, o método chamado é o excluir.
  • Entre as linhas 66 e 94 está definido o componente wdlgDetalhe, que possui os campos para edição e um commandLink que chama o método gravar do controlador na linha 83 (por enquanto não foi implementado o método cancelar - basta fechar a janela).
  • No começo do arquivo, entre as linhas 11 e 17, uma sobreposição do CSS utilizado pelo Primefaces, para tirar as bordas do p:panelGrid da linha 73 e o scroll do p:dialog da linha 66.

Já no controlador, os métodos são bem tranquilos, ressaltando que para atualizar a tabela, somos obrigados a esvaziar a lista autores (linhas 38 e 43), o que automaticamente causa uma nova leitura dos dados a partir do banco (linhas 63, 64 e 65). Essa implementação funciona, mas é justamente essa nova leitura do banco de dados que vamos evitar, na sequência dessa série de posts. Também há um efeito indesejado que ocorre entre os clientes que estiverem acessando a aplicação: os dados atualizados só aparecem se ocorrer uma operação de CRUD na aplicação, ou seja, após uma edição, ou após excluir um registro. Esse comportamento será substituído pela atualização automática, no próximo post.

Nenhum comentário:

Postar um comentário