CRUD JSF 2.0 + Hibernate: Parte 2– Utilizando a biblioteca Primefaces no Gerenciador de Livros

13 09 2010

Este blog possui um novo domínio agora jamacedo.com, clique aqui para acessá-lo!

Depois de algum tempo sem postar nada devido a adaptação ao Rio e a empresa que comecei a trabalhar, Concrete Solutions, volto com muitas ideias novas e tecnologias diferentes para compartilhar aqui no blog. Continuarei escrevendo sobre desenvolvimento java para web mas tratarei também de assuntos como arquitetura orientada a serviço (SOA), projeto e análise de serviços, padrões de projeto empresariais, bibliotecas java script e assuntos referentes ao mercado de trabalho de tecnologia.

Vamos descrever neste artigo algumas mudanças no nosso CRUD JSF 2 para que ele passe a interagir com o usuário utilizando ajax, também vamos melhorar a interface do CRUD com a biblioteca de componentes JSF PrimeFaces. Esta biblioteca de componentes foi uma das primeiras a dar suporte a nova versão do JSF, além de estar sendo constantemente melhorada pelo seu autor Cagatay Civici. Uma das últimas mudanças foi a possibilidade da escolha de diversos skins para os componentes, assim como a possibilidade de utilização do suporte de criação de skin do jquery para gerar skins personalizados para os componentes do PrimeFaces através do ThemeRoller. Utilizaremos skin também neste post.

O CRUD passará a ter a seguinte interface:

Pré Requisitos:

Avisos:

  • O desenvolvimento dos códigos foi realizado no Netbeans 6.9, porém você pode utilizar a IDE de sua preferência para construir o CRUD. Os projeto disponíveis para download no fim do tutorial é executável no Netbeans.
  • A versão do PrimeFaces utilizada foi a 2.2, utilize esta ou uma superior.

Adicionando e configurando a biblioteca PrimeFaces

Após  baixar a biblioteca PrimeFaces você deve adicioná-la ao projeto do CRUD de Livros. Caso não tenha ainda o projeto, baixe-o por aqui ou acompanhe a primeira paste do CRUD aqui.

A configuração necessária para que o PrimeFaces funcione é ZERO, isso mesmo, não é necessário nenhuma configuração xml para começar a utiliza-lo, apenas a sua declaração na página que o utilizar. No nosso caso a página index.xhtml deverá conter a seguinte declaração:

xmlns:p="http://primefaces.prime.com.tr/ui"

Para que possa ser utilizado um skin diferente na página, é necessário a inserção do seguinte código no arquivo web.xml

    <context-param>
        <param-name>primefaces.skin</param-name>
        <param-value>none</param-value>
    </context-param>

Esta configuração remove o skin padrão do PrimeFaces e permite que outros skins possam ser usados, sendo que, a especificação ocorre na página através da adição da tag <link>. Você deve clicar aqui para baixar o skin que é utilizado nas modificações deste CRUD. A pasta do skin deve ser colocada na pasta web do seu projeto. E as páginas que forem utilizar o skin devem conter a seguinte chamada dentro da tag <head>:

<link type="text/css" rel="stylesheet" href="dot-luv/skin.css" />

Porque utilizar ajax no nosso CRUD de Livros?

O ajax é a maneira que foi encontrada para que não fosse necessário recarregar toda uma página quando houvesse algumas mudanças apenas em parte dela. O objetivo do uso de ajax é tornar as páginas web mais próximas de softwares desktop e com isso poder ter uma aplicação que não depende de uma máquina específica e que pode ser acessada de qualquer lugar. Isso implica também em um novo modelo de negócio que começa a se difundir cada vez mais chamado Software as a Service (SaaS), ou, Software como um Serviço, este modelo se baseia em oferecer um serviço a diversos usuários pela web. Para o usuário a diferença está em não pagar um alto custo inicial pelo software como no modelo tradicional, além de poder acessar seu aplicativo de qualquer lugar. Para a empresa desenvolvedora a diferença está no lucro que não virá de uma vez como no modelo tardicional e também na facilidade de manutenção e updates para os usuários, pois, precisará realizadar apenas uma vez. Acho que comecei a me desviar um pouco do foco então vamos voltar. Em outro momento oportuno escreverei sobre SaaS.

Utilizando os componentes PrimeFaces no CRUD

Para melhorar a aparência do CRUD e adicionar a chamada assincrona por ajax aos métodos nós vamos adicionar alguns componentes da biblioteca PrimeFaces. Entre os componentes utilizados estão o layout, dataTable, commandLink, commandButton e o dialog.

Além de algumas mudanças nos códigos, nos descartaremos a página gerenciarLivro.xhtml e seu conteúdo irá para a página index.xhtml. Este conteúdo ficará dentro do componente dialog do primefaces e será chamado quando for necessário incluir ou alterar um livro.

Vamos ver como a página index.xhtml fica e depois vamos entender um poucos das mudanças realizadas.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.prime.com.tr/ui">
    <h:head>
        <title>Gerenciador de Livros</title>
        <link type="text/css" rel="stylesheet" href="dot-luv/skin.css" />
    </h:head>
    <h:body>
        <p:layout fullPage="true">
            <p:layoutUnit position="left" width="200" header="Atividades" resizable="true" closable="true" collapsible="true">
                <h:form prependId="false">
                    <p:commandLink value="Novo Livro" actionListener="#{livroController.prepararAdicionarLivro}" update="infosLivro" oncomplete="dialogGerLivro.show()"/>
                </h:form>
            </p:layoutUnit>
            <p:layoutUnit position="center">
                <h1>Gerenciador de Livros</h1>
                <br/>
                <h:form prependId="false">
                    <p:dataTable id="tabela" var="livro" value="#{livroController.listarLivros}">
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Título"/>
                            </f:facet>
                            <h:outputText value="#{livro.titulo}" />
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Autor"/>
                            </f:facet>
                            <h:outputText value="#{livro.autor}"/>
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Páginas"/>
                            </f:facet>
                            <h:outputText value="#{livro.paginas}"/>
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Editora"/>
                            </f:facet>
                            <h:outputText value="#{livro.editora}"/>
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Isbn"/>
                            </f:facet>
                            <h:outputText value="#{livro.isbn}"/>
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Avaliação"/>
                            </f:facet>
                            <h:outputText value="#{livro.avaliacao}"/>
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Alterar"/>
                            </f:facet>
                            <p:commandButton actionListener="#{livroController.prepararAlterarLivro}" value="Alterar" update="infosLivro" oncomplete="dialogGerLivro.show()"/>
                        </p:column>
                        <p:column>
                            <f:facet name="header">
                                <h:outputText value="Excluir"/>
                            </f:facet>
                            <h:commandLink action="#{livroController.excluirLivro}" value="Excluir"/>
                        </p:column>
                    </p:dataTable>
                </h:form>
            </p:layoutUnit>
        </p:layout>

        <p:dialog header="Gerencia de Livro" widgetVar="dialogGerLivro"  resizable="false" modal="true" showEffect="slide" width="500">
            <h:form prependId="false">
                <h:panelGrid id="infosLivro" columns="2" style="margin-bottom:10px">

                    <h:outputLabel for="titulo" value="Título:" />
                    <h:inputText id="titulo" value="#{livroController.livro.titulo}"/>

                    <h:outputLabel for="autor" value="Autor:" />
                    <h:inputText id="autor" value="#{livroController.livro.autor}"/>

                    <h:outputLabel for="paginas" value="Páginas:" />
                    <h:inputText id="paginas" value="#{livroController.livro.paginas}"/>

                    <h:outputLabel for="editora" value="Editora:" />
                    <h:inputText id="editora" value="#{livroController.livro.editora}"/>

                    <h:outputLabel for="isbn" value="ISBN:" />
                    <h:inputText id="isbn" value="#{livroController.livro.isbn}"/>

                    <h:outputLabel for="avaliacao" value="Avaliação:" />
                    <h:selectOneMenu id="avaliacao" value="#{livroController.livro.avaliacao}">
                        <f:selectItem itemLabel="1" itemValue="1"/>
                        <f:selectItem itemLabel="2" itemValue="2"/>
                        <f:selectItem itemLabel="3" itemValue="3"/>
                        <f:selectItem itemLabel="4" itemValue="4"/>
                        <f:selectItem itemLabel="5" itemValue="5"/>
                    </h:selectOneMenu>

                    <p:commandButton update="tabela" oncomplete="dialogGerLivro.hide();" actionListener="#{livroController.adicionarLivro}" value="Inserir Livro"/>
                    <p:commandButton update="tabela" oncomplete="dialogGerLivro.hide();" actionListener="#{livroController.alterarLivro}" value="Alterar Livro"/>

                </h:panelGrid>
            </h:form>
        </p:dialog>

    </h:body>
</html>

O componente layout contém um subcomponente chamado layoutUnit. Nas configurações deste subcomponente nós informamos onde o código que estiver dentro dele vai se localizar na página. Por exemplo utilizando o position=”left” estamos falando que todo código dentro dete componente ficará do lado esquerdo da tela. Além disso, definimos a configuração fullPage do layout como true, o que faz o layout preencher toda a tela do browser.

A principal mudança do dataTable está na troca dos componentes padrões do jsf pelo dataTable do primefaces. Isso permite que a tabela seja renderizada e seja alterada com o uso do skin, além de muitas outras funcionalidades que não serão abordadas neste post. O dataTable passou a ter um id que serve para identifica-lo para que  fosse possível atualizar a tabela por ajax.

Os commandLinks e os commandButtons tiveram grandes alterações, pois é através deles que as chamadas ajax são realizadas. Vamos ver as principais mudanças:

  • Troca do action pelo actionListener: esta troca é necessária porque não vamos mais carregar outra página após a execução de um método no servidor. Nós vamos executar os métodos no servidor e depois apenas atualizar os componentes que sofreram mudanças com a execução do método.
  • O uso do atributo update informa quais componentes deverão ser renderizados novamente após a execução do actionListener, com isso o usuário passa a ter a sensação que está utilizando um programa desktop e não simplismente acessando um site.
  • O atributo onComplete serve para ativar o dialog que apresenta as informações de um livro para alterações ou inclusões. A ativação do dialog só ocorre após a execução do método que estiver definido no actionListener e também após a renderização dos componentes que estão na variável update.

O componente dialog permite a criação de uma janela modal que pode ser arrastada pela tela e torna a aplicação mais próxima ainda de aplicações desktop, pelo fato das janelas serem comuns nas aplicações desktop. Uma variável importante deste componente é o widgetVar que serve para definir um nome para o dialog. Com este nome o dialog pode ser apresentado ou escondido na tela através das funções javascript nomeDoDialog.show() e nomeDoDialog.hide(), respectivamente.

Estas são as informações importantes para compreender as mudanças realizadas nas páginas xhtml. Agora vamos ver como a classe  LivroController.java deve ficar para que tudo funcione corretamente.

package Controller;

import Dao.LivroDao;
import Dao.LivroDaoImp;
import Model.Livro;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;

/**
 * @author José Alexandre
 */
@ManagedBean
@SessionScoped
public class LivroController {

    private Livro livro;
    private DataModel listaLivros;

    public DataModel getListarLivros() {
        List<Livro> lista = new LivroDaoImp().list();
        listaLivros = new ListDataModel(lista);
        return listaLivros;
    }

    public Livro getLivro() {
        return livro;
    }

    public void setLivro(Livro livro) {
        this.livro = livro;
    }

    public void prepararAdicionarLivro(ActionEvent actionEvent){
        livro = new Livro();
    }

    public void prepararAlterarLivro(ActionEvent actionEvent){
        livro = (Livro)(listaLivros.getRowData());
    }

    public String excluirLivro(){

        Livro livroTemp = (Livro)(listaLivros.getRowData());
        LivroDao dao = new LivroDaoImp();
        dao.remove(livroTemp);
        return "index";

    }

    public void adicionarLivro(ActionEvent actionEvent){

        LivroDao dao = new LivroDaoImp();
        dao.save(livro);

    }

    public void alterarLivro(ActionEvent actionEvent){

        LivroDao dao = new LivroDaoImp();
        dao.update(livro);

    }

}

A mudança realizada nesta classe está nos métodos deixarem de retornar uma String com a página de destino. Eles passam a não retornar nada e também passam a receber como parâmetro um objeto ActionEvent. Isso acontece porque o objetivo agora é apenas realizar algumas mudanças no servidor e depois atualizar somente algumas partes da página através de ajax.

Essas foram as mudanças necessárias no CRUD para que ele passasse a utilizar ajax e alguns dos componentes da biblioteca primefaces.

Conclusão

Podemos notar que com poucas mudanças tivemos um ganho enorme com relação a usabilidade e aparência do CRUD. A partir deste CRUD o usuário pode começar a explorar os diversos outros componentes disponibilizados pelo primefaces, veja aqui os demos de todos eles, lembrando que existem também outras bibliotecas de componentes que o usuário pode conhecer e que podem atender melhor as necessidades de componentes de acordo com o caso.

Espero que tenham aproveitado e entendido o funcionamento do uso dos componentes primefaces e do ajax!

Por hoje é só e até a próxima! Qualquer dúvida estou a disposição!

Download

Para baixar o código fonte compatível com Netbeans clique aqui.








Seguir

Obtenha todo post novo entregue na sua caixa de entrada.