Utilizando componentes com Drag and Drop no PrimeFaces

Uma importante funcionalidade do framework JavaServer Faces (JSF) é a possibilidade de arrastar os componentes dentro da tela, o chamado Drag and Drop, que é o foco deste artigo.

O JSF é uma especificação para o desenvolvimento de aplicações Web, seguindo o padrão Model View Controler (MVC) em Java, essa especificação surgiu como uma alternativa ao Struts, que na época era o principal framework para implementar aplicações nesse padrão de projeto. Sua primeira versão foi disponibilizada em 2004, e em 2013 foi lançada a versão 2.2.

Atualmente, existem diversos frameworks para construção de interfaces ricas com o JSF, entre eles, um dos principais é o PrimeFaces que disponibiliza diversos componentes. Atualmente esse framework está na versão 5.3 e contém um grande número de componentes de formulários, listagem, menus, entre outros.

Este artigo mostrara como configurar uma aplicação com JSF e PrimeFaces, e também como possibilitar que diversos componentes sejam arrastados dentro da tela, utilizando o conceito de Drag and Drop. Isso pode ser utilizado em diversas situações, como por exemplo, para permitir que um usuário mude a disposição de elementos da tela, ou para mostrar uma lista geral e uma lista de elementos selecionados.

Configurando o projeto

Para facilitar a configuração do projeto, será utilizado o Maven. Para a criação desse projeto serão necessárias as dependências das bibliotecas do PrimeFaces e também do JSF. Para a biblioteca do PrimeFaces também é necessário adicionar as informações do repositório desse framework. A Listagem 1 mostra o arquivo pom.xml do projeto criado.

Listagem 1. Configurando o projeto com o PrimeFaces 5.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.santana.devmedia</groupId> <artifactId>primefaces</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>primefaces Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>5.1</version> <scope>compile</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>javax.servlet.jsp-api</artifactId> <version>2.2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.faces</artifactId> <version>2.1.13</version> <scope>compile</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <repositories> <repository> <id>prime-repo</id> <name>PrimeFaces Maven Repository</name> <url>http://repository.primefaces.org</url> <layout>default</layout> </repository> </repositories> <build> <finalName>primefaces</finalName> </build> </project>

Depois de configurar as dependências do projeto, é necessário configurar a aplicação para usar o framework JSF, como o projeto é de uma aplicação Web, necessitamos criar o arquivo web.xml. O passo mais importante, é configurar o Faces Servlet, que utiliza a classe javax.faces.webapp.FacesServlet, e configurar o mapeamento das URL’s que serão tratadas pelo JSF, no caso, todos os endereços que terminarem com *.xhtml. Também foi configurado que caso acontece algum erro na aplicação, a requisição seja redirecionada para a página inicial da aplicação. A Listagem 2 mostra o código do arquivo web.xml.

Listagem 2. Configuração do Web.xml do projeto.

<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>com.devmedia.primefaces.draganddrop</display-name> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>/index.xhtml</welcome-file> </welcome-file-list> <error-page> <exception-type>javax.faces.application.ViewExpiredException</exception-type> <location>/index.xhtml</location> </error-page> </web-app>

Para executar os exemplos criados neste artigo será necessário utilizar um container Web, o PrimeFaces pode ser executado em qualquer um que seja compatível com o Java Web, entre eles o Jetty e o Tomcat. No desenvolvimento do artigo foi utilizado o Apache Tomcat, versao 8.0.12.

Drag and Drop

Em qualquer componente, ou conjunto de componentes, do Prime Faces pode ser adicionado a funcionalidade de Drag em Drop, para isso é utilizada a tag <p:draggable>, que possibilita que esse componente seja arrastado para qualquer lugar dentro da tela. Por exemplo, na Listagem 3 é criado um formulário para o cadastro de pessoas, esse formulário foi colocado dentro de um panelGrid. Logo abaixo do panelGrid, está a tag indicando que ele pode ser arrastado dentro da tela, a configuração é extremamente simples, basta passar o id do componente que se quer arrastar para o atributo for da tag.

Listagem 3. Drag and Drop simples em um panelGrid.

<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.org/ui"> <h:head> </h:head> <h:body> <h:form> <p:panelGrid columns="2" id="pnl" header="Draggable Panel"> <h:outputText id="lblNome" value="Nome:" /> <h:inputText id="txtNome" /> <h:outputText id="lblIdade" value="Idade:" /> <h:inputText id="txtIdade" /> <h:inputText id="lblEndereco" value="Endereço:" /> <h:inputText id="txtEndereco" /> <h:commandButton id="btCadastrar" value="Cadastrar" /> </p:panelGrid> <p:draggable for="pnl" /> </h:form> </h:body> </html>

A Figura 1 mostra o formulário desenvolvido, a tela é bastante simples, apenas com o formulário, seus três campos e o botão de cadastro. Não é possível mostrar aqui, porém o usuário pode mover esse formulário livremente dentro da tela da aplicação.

Figura 1. Tela em que está sendo feita o Drag and Drop.

A tag <p:draggable> tem diversas opções para configurar como o componente pode ser arrastado dentro da tela, como por exemplo o atributo axis, que indica que o componente pode ser arrastado apenas na horizontal, ou apenas na vertical. Esse atributo pode receber o valor x ou y, indicando qual eixo a movimentação pode ser feita. A Listagem 4 mostra um exemplo, onde o mesmo formulário do exemplo anterior apenas pode ser movimentado no eixo x.

Listagem 4. Movimentação do formulário no eixo X.

<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.org/ui"> <h:head> </h:head> <h:body> <h:form> <p:panelGrid columns="2" id="pnl" header="Draggable Panel"> <h:outputText id="lblNome" value="Nome:" /> <h:inputText id="txtNome" /> <h:outputText id="lblIdade" value="Idade:" /> <h:inputText id="txtIdade" /> <h:inputText id="lblEndereco" value="Endereço:" /> <h:inputText id="txtEndereco" /> <h:commandButton id="btCadastrar" value="Cadastrar" /> </p:panelGrid> <p:draggable for="pnl" axis="x" /> </h:form> </h:body> </html>

Outra opção interessante é permitir que o componente seja arrastado apenas dentro de um compartimento especifico da tela. Para isso, pode ser implementado um painel, e permitir que o outro componente seja arrastado apenas dentro desse painel pai. A Listagem 5 mostra um exemplo disso, nele, é colocado um <p:outputPanel>, e o mesmo formulário dos exemplos anteriores são colocados dentro desse painel, e com o atributo containement da tag <p:draggable> é colocado que o componente pode ser movimentado apenas dentro de seu componente pai. Observe o resultado na Figura 2.

Listagem 5. Componente que só pode ser arrastado dentro do painel.

<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.org/ui"> <h:head> </h:head> <h:body> <h:form> <p:outputPanel id="restrictPanel" layout="block" > <p:panelGrid columns="2" id="pnl" header="Draggable Panel"> <h:outputText id="lblNome" value="Nome:" /> <h:inputText id="txtNome" /> <h:outputText id="lblIdade" value="Idade:" /> <h:inputText id="txtIdade" /> <h:inputText id="lblEndereco" value="Endereço:" /> <h:inputText id="txtEndereco" /> <h:commandButton id="btCadastrar" value="Cadastrar" /> </p:panelGrid> </p:outputPanel> <p:draggable for="pnl" containment="parent" /> </h:form> </h:body> </html>

Figura 2. Formulário só pode ser arrastado dentro do painel pai.

Outra possibilidade com o drag and drop é fazer elementos de uma tabela serem arrastados para outra tabela, por exemplo, se na minha aplicação eu tiver uma lista de funcionários, e quiser arrastar os funcionários que serão alocados em um projeto. Para isso, o PrimeFaces permite arrastar elementos de um dataTable para outro, através da tag <p:droppable>. Para esse exemplo serão necessárias algumas classes a mais, primeiro é necessário definir a classe Pessoa com os atributos id, nome e idade. A Listagem 6 mostra o código dessa classe. Além dos atributos são também definidos um construtor com todos os atributos da classe, e os métodos assessores para os atributos.

Listagem 6. Classe Pessoa com seus atributos, construtor e os métodos get/set

package com.santana.devmedia.primefaces.classes; public class Pessoa { private int id; private String nome; private int idade; public Pessoa(int id, String nome, int idade) { super(); this.id = id; this.nome = nome; this.idade = idade; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public int getIdade() { return idade; } public void setIdade(int idade) { this.idade = idade; } }

Além da classe pessoa, é necessário implementar o ManagedBean para gerenciar a lista de todas as pessoas e a lista de pessoas selecionadas. Para isso foi definida a classe PessoaMB, que está na Listagem 7, e tem as duas listas. Inicialmente são colocados cinco elementos na lista pessoas (aqui elas foram criadas manualmente, mas poderiam ter sido recuperadas de qualquer fonte de dados). Depois, o método mais importante é o onDrop, que é o método que será executado quando uma pessoa for arrastada de uma tabela para outra.

Listagem 7. ManagedBean para gerenciar as listas de pessoas

package com.santana.devmedia.primefaces.classes; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import org.primefaces.event.DragDropEvent; @ManagedBean(name = "PessoasMB") @ViewScoped public class PessoaMB { private List<Pessoa> pessoas; private List<Pessoa> pessoasSelecionadas; @PostConstruct public void init() { // cria algumas pessoas para o teste Pessoa p1 = new Pessoa(1, "Eduardo", 29); Pessoa p2 = new Pessoa(2, "Brianda", 30); Pessoa p3 = new Pessoa(3, "Luiz", 32); Pessoa p4 = new Pessoa(4, "Bruna", 26); Pessoa p5 = new Pessoa(5, "Sonia", 58); pessoas = new ArrayList<Pessoa>(); pessoas.add(p1); pessoas.add(p2); pessoas.add(p3); pessoas.add(p4); pessoas.add(p5); pessoasSelecionadas = new ArrayList<Pessoa>(); } // método que será executado quando for feito o drag and drop public void onDrop(DragDropEvent ddEvent) { Pessoa pessoa = ((Pessoa) ddEvent.getData()); pessoasSelecionadas.add(pessoa); pessoas.remove(pessoa); } public List<Pessoa> getPessoas() { return pessoas; } public List<Pessoa> getPessoasSelecionadas() { return pessoasSelecionadas; } }

Por fim, é definido o código da tela onde serão exibidas as tabelas de pessoas, a primeira que lista todas as pessoas, e a segunda que lista as pessoas que foram arrastadas para essa tabela. Quando uma pessoa é arrastada de uma tabela para outra, ela deve sair da tabela de origem e aparecer apenas na tabela de destino.

Na Listagem 8 é definido o código, nele inicialmente é definido um <p:fieldset>, que é apenas um painel com um título no alto. Nele é definido uma <p:dataTable> chamado todasPessoasTabela, nela são listadas todas as pessoas que estão na aplicação, essa é a tabela de origem. Dentro da primeira coluna dessa tabela é definido o item que será possível arrastar com a tag <p:draggable>.

A outra tabela também é colocada dentro de um <p:fieldset>, essa tabela é chamada pessoasSelecionadasTabela, ela tem o mesmo formato da tabela anterior, a diferença é que ela começa vazia, esperando que pessoas sejam arrastadas para ela. Para permitir que isso aconteça, é definida a tag <p:droppable> que indica qual é a fonte dos dados, qual é o destino, e quais componentes da tela devem ser atualizados quando ocorrer o drag and drop, no caso desse exemplo, as duas tabelas.

Listagem 8. Tela com os dois data tables.

<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.org/ui"> <h:head> </h:head> <h:body> <script type="text/javascript"> function handleDrop(event, ui) { var droppedCar = ui.draggable; droppedCar.fadeOut('fast'); } </script> <h:form id="pessoaForm"> <p:fieldset id="todasPessoas" legend="Todas as Pessoas"> <p:dataTable id="todasPessoasTabela" var="pessoa" value="#{PessoasMB.pessoas}"> <p:column > <h:outputText id="dragIcon" styleClass="ui-icon ui-icon-arrow-4" /> <p:draggable for="dragIcon" revert="true" helper="clone" /> </p:column> <p:column headerText="Id"> <h:outputText value="#{pessoa.id}" /> </p:column> <p:column headerText="Nome"> <h:outputText value="#{pessoa.nome}" /> </p:column> <p:column headerText="Idade"> <h:outputText value="#{pessoa.idade}" /> </p:column> </p:dataTable> </p:fieldset> <p:fieldset id="pessoasSelecionadas" legend="Pessoas Selecionadas" > <p:outputPanel id="dropArea"> <h:outputText value="Coloque pessoas aqui!" rendered="#{empty PessoasMB.pessoasSelecionadas}" /> <p:dataTable id="pessoasSelecionadasTabela" var="pessoa" value="#{PessoasMB.pessoasSelecionadas}" rendered="#{not empty PessoasMB.pessoasSelecionadas}"> <p:column headerText="Id"> <h:outputText value="#{pessoa.id}" /> </p:column> <p:column headerText="Nome"> <h:outputText value="#{pessoa.nome}" /> </p:column> <p:column headerText="Idade"> <h:outputText value="#{pessoa.idade}" /> </p:column> </p:dataTable> </p:outputPanel> </p:fieldset> <p:droppable for="pessoasSelecionadas" tolerance="touch" activeStyleClass="ui-state-highlight" datasource="todasPessoasTabela" onDrop="handleDrop"> <p:ajax listener="#{PessoasMB.onDrop}" update="dropArea todasPessoasTabela" /> </p:droppable> </h:form> </h:body> </html>

A Figura 3 mostra a configuração inicial da aplicação, onde vocês podem observar que a lista de todas as pessoas está com as cinco pessoas definidas no ManagedBean. Para fazer o drag and drop o usuário deve selecionar o ícone da primeira coluna da tabela e arrastar para a segunda tabela da tela. Quando o item estiver sendo arrastado, a segunda tabela será destacada na tela, isso é feito pelo método javascript handleDrop, definido no começo da Listagem 8.

Figura 3. Configuração inicial da aplicação.

A Figura 4 já mostra a tela depois de duas pessoas terem sido arrastadas para a lista de pessoas selecionadas. Sempre que uma pessoa é arrastada de uma tabela para outras, as listas definidas no ManagedBean são atualizadas, por isso, ao serem atualizadas as tabelas mostram os estados atuais dessas listas.

Figura 4. Duas pessoas arrastadas para a lista de pessoas selecionadas.

Esse tipo de aplicação é bastante utilizado quando o usuário deve selecionar diversos elementos de uma lista. Esse tipo de interface é bastante amigável com o usuário e bastante intuitiva também. Com o PrimeFaces isso é também muito fácil de implementar.

Este artigo mostrou como utilizar o drag and drop em diversos componentes do Primefaces. O projeto utilizou a versão mais recente do PrimeFaces, que é hoje um dos principais frameworks para a criação de interfaces ricas do JSF.

Espero que este artigo seja útil. Até a próxima!

Links

PrimeFaces
http://www.primefaces.org

PrimeFaces – ShowCase
http://www.primefaces.org/showcase/

JavaServer Faces
http://www.oracle.com/technetwork/java/javaee/javaserverfaces-139869.html

Artigos relacionados