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.

Tela em que está sendo feita o Drag and Drop

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"
                    style="width:600px;height:400px;border:1px solid #666666;margin-bottom:20px">
   
                    <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>

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

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 style="width:20px">
                                <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"
                    style="margin-top:20px">
                    <p:outputPanel id="dropArea">
                          <h:outputText value="Coloque pessoas aqui!"
                                rendered="#{empty PessoasMB.pessoasSelecionadas}" style="font-size:24px;" />
                          <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.

Configuração inicial da aplicação

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.

Duas pessoas arrastadas para a lista de pessoas
selecionadas

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