A parte anterior do JSF 2 fu , um pré-requisito para este artigo, apresentou os recursos do HTML5 e mostrou como construir um componente composto de JSF que torna fácil utilizar telas HTML5. Neste artigo, trabalho na implementação de dois componentes compostos de JSF — <h5:drag> e<h5:drop> — que aproveitam o mecanismo de arrastar e soltar baseado em evento do HTML5, (consulte Recursos ).
Os componentes compostos de arrastar e soltar apresentam cinco recursos significativos:
- Fácil de usar
- Arrasto condicional
- Ajax
- Renderização parcial
- Carga útil
No nível mais fundamental, <h5:drag> e <h5:drop> encapsulam algumas das excentricidades do arrastar e soltar do HTML5 para aprimorar a facilidade de uso. Por exemplo, os navegadores rejeitam arrastar e soltar por padrão, assim nos manipuladores de eventos drag-enter e drag-over é necessário cancelar a reação padrão do navegador cancelando o evento. Essa armadilha não intuitiva é tratada com o componente <h5:drag> , que permite que os autores de páginas concentrem-se em questões mais importantes.
Os componentes <h5:drag> e <h5:drop> suportam arraste condicional. O autor de página pode aceitar ou rejeitar os descartes com base nos dados que estão sendo transferidos e o destino do descarte em questão.
Em aplicativos JSF, a maior parte dos dados que os usuários manipulam está armazenada no servidor, geralmente como beans gerenciados. Por essa razão, o componente <h5:drop> faz uma chamada Ajax ao aceitar um descarte. Os autores de página podem especificar quais componentes o JSF irá renderizar quando a chamada Ajax retornar.
Os componentes <h5:drag> e <h5:drop> também suportam anexar determinados dados —
geralmente denominados de carga útil — para um gesto de arrastar e soltar. Os autores de página especificam uma propriedade de bean que serve como a carga útil. Durante a chamada Ajax, iniciada pelo componente <h5:drop> quando ocorrer um descarte, o JSF chama o método setter para a propriedade de bean da carga útil. O JSF trata cargas úteis de arrastar e soltar, portanto, como valores de elementos <h:inputText> .
Usando origens de arrasto e destinos de descarte
<h5:drag> e <h5:drop> representam fontes de arrasto HTML5 e destinos de descarte, respectivamente. Em um aplicativo JSF que usar esses componentes, use origens de arrasto desta maneira:
<script>
function dragStart(event) {
event.dataTransfer.setData('text', "transfer this string");
}
</script>
<h5:drag ondragstart="dragStart(event)">
...
</h5:drag>
|
O autor de página pode colocar alguns componentes ou elementos HTML no componente <h5:drag> e definir os dados da transferência na função ondragstart , como na marcação anterior.
Use destinos de descarte desta maneira:
<h5:drop id="dropzone"
carga útil="#{dragDrop.payload}"
render="@this"
...
</h5:drop>
|
Como nas origens de arrasto, o autor de página pode colocar alguns componentes ou elementos HTML em <h5:drop> . O autor de página especifica uma propriedade de bean da carga útil de arrastar e soltar e especifica quais componentes o JSF deve renderizar quando a chamada Ajax, executada após um descarte, retornar do servidor.
Os autores de página também podem intervir opcionalmente em um gesto de arrastar e soltar com algum JavaScript:
<h5:drop id="dropzone"
payload="#{dragDrop.payload}"
render="@this"
ondragover="dragover(event)"
ondragenter="dragenter(event)"
ondragleave="dragleave(event)"
ondrop="drop(event)">
...
</h5:drop>
|
Na marcação anterior, as funções dragover(), dragenter(), dragleave()e pela drop() (não mostradas) são implementadas pelo autor da página.
Agora que sabe onde esse artigo se encaixa, discutiremos o caso de uso de arrastar e soltar desse artigo: o aplicativo Feeds.
O aplicativo Feeds, mostrado na Figura 1, é um leitor de alimentação RSS. O menu esquerdo exibe uma lista de alimentações RSS e permite aos usuários adicionar alimentações à lista. O meio da página mostra os links do artigo para a alimentação atual. Quando o usuário clica no link de um artigo, o aplicativo carrega o artigo associado no navegador e o usuário poderá subsequentemente pressionar o botão Voltar para retornar ao aplicativo.
Figura 1. O aplicativo Feeds
Uma lista de artigos de qualquer alimentação RSS é atualizada regularmente, logo, usuários ocupados podem salvar links para futura leitura ao arrastar links de artigos do centro do aplicativo para o menu à direita, conforme mostrado na Figura 2:
Figura 2. Arrastar e soltar no aplicativo Feeds
O aplicativo possui um bean gerenciado, mostrado na Listagem 1, que lê uma alimentação ESS e fornece uma lista subsequente de itens RSS:
Listagem 1. Recuperando e analisando alimentações RSS
package com.clarity;
import java.io.Serializable;
import java.net.URL;
import java.util.LinkedList;
import org.gnu.stealthp.rsslib.RSSChannel;
import org.gnu.stealthp.rsslib.RSSHandler;
import org.gnu.stealthp.rsslib.RSSItem;
import org.gnu.stealthp.rsslib.RSSParser;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named("rssFeed")
@SessionScoped
public class RSSFeed implements Serializable {
private static final long serialVersionUID = 2L;
private String feed, displayName;
private RSSChannel channel;
private LinkedList<RSSItem> savedItems = new LinkedList<RSSItem>();
public void fetch(String f, String dn) {
assert f != null;
assert dn != null;
feed = f;
displayName = dn;
RSSHandler handler = new RSSHandler();
channel = handler.getRSSChannel();
try {
RSSParser.parseXmlFile(new URL(feed), handler, true);
} catch (Exception e) {
channel = null;
e.printStackTrace();
}
}
publicLinkedList<RSSItem> getItems() {
return channel == null ? null : channel.getItems();
}
public LinkedList<RSSItem> getSavedItems() {
return savedItems;
}
public RSSChannel getChannel() { return channel; }
public String getFeed() { return feed; }
public String getDisplayName() { return displayName; }
} |
A classe RSSFeed usa RSSLib4J, o que torna fácil recuperar e analisar uma alimentação RSS (consulte Recursos ). Devido aos atributos @Named e @SessionScoped na listagem 1, o aplicativo tem um bean gerenciado com escopo de sessão denominado rssFeed que é uma instância de RSSFeed.
Todos os links no menu esquerdo do aplicativo têm ações que chamam rssFeed.fetch(). Por exemplo, o link Apple no menu esquerdo é implementado desta maneira:
<h:commandLink value="Apple"
action="#{rssFeed.fetch('http://rss.news.yahoo.com/rss/applecomputer',
'Apple Computer')}"/>
|
Quando o usuário clica no link, o JSF chama o método rssFeed fetch() do bean gerenciado e subsequentemente recarrega a lista de links mostrada no meio do aplicativo:
<ui:repeat value="#{rssFeed.items}" var="item">
<a href="#{item.link}">#{item.title}</a>
<ui:repeat>
|
O aplicativo também exibe os links salvos no menu direito:
<ui:repeat value="#{rssFeed.savedItems}" var="item">
<a href="#{item.link}">
#{ fn:substring(#{item.title}, 0, 25) } ...
</a>
<ui:repeat>
|
Agora que você viu como o aplicativo Feeds recupera e exibe itens de alimentações RSS, voltarei minha atenção ao evento principal deste artigo.
Os componentes <h5:drag> e <h5:drop>
Os componentes <h5:drag> e <h5:drop> são implementados em três arquivos, um de cada (drag.xhtml e drop.xhtml) para os componentes e outro (drop.js) para o JavaScript do componente <h5:drop> , como mostrado na Figura 3:
Figura 3. Arquivos dos componentes
<h5:drop> e <h5:drag> :
Embora os componentes tornem fácil arrastar e soltar Ajaxfield, suas implementações são modestas. Os dois componentes são implementados inteiramente com marcação facelets e JavaScript: cerca de 100 linhas de marcação e 50 linhas de JavaScript. Para fins ilustrativos, explorarei o desenvolvimento em três iterações:
- Implementar arrastar e soltar no cliente
- Adicionar chamadas Ajax quando ocorrer descartes
- Suporta arrastamento condicional
Para começar, irei me concentrar no cliente. Quando o usuário solta um link no destino de descarte, apenas mostro um alerta que exibe o título do artigo seguido por sua URL, como mostrado na Figura 4:
Figura 4. Alerta exibindo um link descartado no destino de descarte
Para ilustrar como implementar arrastar e soltar como mostrado na Figura 4, discutirei os seguintes artefatos:
- Origem de arrasto
- Componente da origem de arrasto
- Destino do descarte
- Componente do destino de descarte
- JavaScript do componente do destino de descarte
Cada link no centro do aplicativo, destacado na Figura 5, é uma fonte de arrasto:
Figura 5. A fonte do arrasto
A Listagem 2 mostra a marcação e o JavaScript da origem do arrasto:
Listagem 2. Marcação e JavaScript da origem do arrasto
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h5="http://java.sun.com/jsf/composite/html5"
xmlns:places="http://java.sun.com/jsf/composite/places">
<script>
// event.target é um dos elementos h5:drag gerados pela função ui:repeat a seguir
dragStart(event) {
var linkref = event.target.firstElementChild.firstElementChild; // anchor
var link = linkref.href;
var title = linkref.textContent;
. dataTransfer.setData('text', title + " | " + link + " ");
}
</script>
<h:panelGrid id="items" columns="1" >
<ui:repeat value="#{rssFeed.items}" var="item">
<h5:drag ondragstart="dragStart(event)">
<p><e href="#{item.link}">#{item.title}</a> <br /></p>
</h5:drag>
</ui:repeat>
</h:panelGrid>
</ui:composition> |
A marcação da Listagem 2usa o componente <h5:drag> , especificando uma função do JavaScript que o JSF chama quando um arrasto é iniciado. O navegador passa um evento para essa função do JavaScript. O destino desse evento é o componente <h5:drag> . A função drill down do elemento <h5:drag> até o elemento âncora e captura o texto e a referência do link. Integra as informações em uma cadeia de caractere com o tipo de transferência de dadostext . A partir daí, o sistema de arrastar e soltar do HTML5 transferirá essa cadeia de caractere para o destino de descarte quando um descarte for aceito.
O componente da origem do arrasto
A implementação do componente <h5:drag> , mostrado na Listagem 3, é simples:
Listagem 3. O componente
<h5:drag>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="ondragstart"/>
</composite:interface>
<composite:implementation>
<div draggable="true"
ondragstart="#{cc.attrs.ondragstart}">
<composite:insertChildren />
</div>
</composite:implementation>
</html> |
O componente <h5:drag> cria um DIV arrastável cujo ondragstart JavaScript é o valor especificado pelo autor de página na listagem 2. O componente também usa <composite:insertChildren> para inserir a marcação que estiver no corpo da tag do <h5:drag> . Em listagem 2, essa marcação é a âncora do link.
O destino de descarte, conforme mostrado na Figura 6, está no menu direito do aplicativo:
Figura 6. O destino de descarte
A Listagem 4 mostra a implementação do destino de descarte:
Listagem 4. O destino de descarte
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:util="http://java.sun.com/jsf/composite/util"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:h5="http://java.sun.com/jsf/composite/html5">
<script>
function drop(event) { alert(event.dataTransfer.getData("text")); }
</script>
<h5:drop id="dropzone"
ondrop="drop(event)">
<div class="welcomeImage">
<h:graphicImage id="welcomeImage"
library="images" name="cloudy.gif"/>
</div>
</h5:drop>
</ui:composition> |
O destino de descarte consiste em um componente <h5:drop> , que contém a imagem da nuvem, quebrado em um DIV. O atributo ondrop do componente <h5:drop> refere-se a uma função do JavaScript que mostra um alerta que contém a cadeia de caractere transferida da origem do arrasto.
O componente do destino de descarte
A Listagem 5 mostra a implementação do componente <h5:drop> :
Listegem 5. O componente
<h5:drop>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="ondragenter"/>
<composite:attribute name="ondragover"/>
<composite:attribute name="ondragleave"/>
<composite:attribute name="ondrop"/>
</composite:interface>
<composite:implementation>
<div id="#{cc.id}" ondragenter="#{cc.attrs.ondragenter}"
ondrop="#{cc.attrs.ondrop}"
ondragover="#{cc.attrs.ondragover}"
ondragleave="#{cc.attrs.ondragleave}">
<composite:insertChildren />
</div>
<script> html5.jsf.init("#{cc.id}"); </script>
</composite:implementation>
</html> |
O componente <h5:drop> , assim como <h5:drag>, cria um DIV e insere a marcação no corpo da tag <h5:drop> para esse DIV.
O componente
<h5:drop> chama html5.jsf.init() quando o componente for criado. Essa função inicializa o DIV do componente ao adicionar manipulador de eventos de drag-enter e drag-over. Esses manipuladores de eventos são implementados na Listagem 6:
Listagem 6. O JavaScript do componente
<h5:drop>
if (!html5)
var html5 = {}
if (!html5.jsf) {
html5.jsf = {
init : function(ccid) {
var dropzone = $(ccid);
dropzone.addEventListener("dragenter", function(event) {
event.preventDefault();
}, false);
dropzone.addEventListener("dragover", function(event) {
event.preventDefault();
}, false);
}
};
} |
O JavaScript na Listagem 6 (no qual apliquei namespace para evitar colisões) impede que o navegador tenha sua reação padrão para eventos de drag-enter e drag-over. Faça rechamada por padrão, o navegador cancela esses eventos, assim o JavaScript na Listagem 6 impede o navegador de rejeitar o descarte. Discutivelmente, é um tanto arcano ter que fazer essas chamadas preventDefault() , mas desse modo, é um bom candidato para encapsular em um componente reutilizável.
Como esse JavaScript impede incondicionalmente o navegador de cancelar eventos de drag-over e drag-enter, aceita incondicionalmente todos os descartes. Isso não o recortará no mundo real, assim, em Arrasto condicional, tratarei disso em breve. A seguir, mostrarei como adicionar Ajax no componente de destino de descarte.
Adicionando Ajax e enviando uma carga útil para o servidor
Em Arrastar e soltar no cliente, tratei de links descartados inteiramente no cliente, ao exibir um alerta mostrando o título e a URL do link (consulte Figura 4). Para o aplicativo Feed e para aplicativos JSF menos triviais que oferecem arrastar e soltar, um descarte geralmente é acompanhado por uma trip até o servidor, onde a carga útil é incorporada em dados no lado do servidor. Devido a esse requisito, nesta seção, adicionei Ajax ao componente <h5:drop> para o componente fazer uma chamada automaticamente quando ocorrer um descarte, enviando a carga útil junto com a chamada Ajax.
Cada vez que um usuário descarta um link no menu direito, o componente <h5:drop> faz uma chamada Ajax, que adiciona o link à lista de links salvos do aplicativo no servidor. Quando a chamada Ajax retornar, o JSF atualiza o destino de descarte para refletir o link recém-adicionado.
A Listagem 7 mostra o destino de descarte atualizado:
Listagem 7. O destino de descarte, Tomada II
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:h5="http://java.sun.com/jsf/composite/html5">
<script>
function dragenter(event) { /* Implement as desired */ }
function dragleave(event) { /* Implement as desired */ }
function dragover(event) { /* Implement as desired */ }
function drop(event) { /* Implement as desired */ }
</script>
<h5:drop id="dropzone" carga útil="#{dragDrop.payload}"
render="@this"
ondragover="dragover(event)"
ondragenter="dragenter(event)"
ondragleave="dragleave(event)"
ondrop="drop(event)">
<div class="welcomeImage">
<h:graphicImage id="welcomeImage"
library="images" name="cloudy.gif"/>
</div>
<br />
<div class="savedItems">
<ui:repeat value="#{rssFeed.savedItems}" var="">
<div class="savedLink">
<a href="#{item.link}">
#{ fn:substring(item.title, 0, 25) } ...
</a>
<br/>
</div>
</ui:repeat>
</div>
</h5:drop>
</ui:composition> |
Nesta versão do destino de descarte, vinculei a carga útil de arrastar e soltar a uma propriedade de bean: #{dragDrop.payload}. E disse ao destino de descarte para renderizar @this — indicando o próprio destino de descarte — quando a chamada Ajax, iniciada por um descarte, retornar.
A Listagem 8 mostra a implementação atualizada do componente <h5:drop> :
Listagem 8. O componente
<h5:drop> , Tomada II
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="ondragenter"/>
<composite:attribute name="ondragover"/>
<composite:attribute name="ondragleave"/>
<composite:attribute name="ondrop"/>
<composite:attribute name="render"/>
<composite:attribute name="carga útil"/>
<composite:attribute name="payloadType"/>
</composite:interface>
<composite:implementation>
<h:outputScript library="javax.faces" name="jsf.js" target="head" />
<h:outputScript library="html5" name="drop.js" target="head" />
<div id="#{cc.id}" ondragenter="#{cc.attrs.ondragenter}"
ondrop="#{cc.attrs.ondrop}"
ondragover="#{cc.attrs.ondragover}"
ondragleave="#{cc.attrs.ondragleave}">
<composite:insertChildren />
<h:form id="form">
<h:inputText id="payload"
value="#{cc.attrs.payload}"
style="display: none"/>
</h:form>
</div>
<script> html5.jsf.init("#{cc.id}",
"#{cc.attrs.payloadType}",
"#{cc.attrs.render}"); </script>
</composite:implementation>
</html> |
O destino de descarte atualizado precisa:
- Fazer uma chamada Ajax quando ocorrer um descarte
- Tornar a carga útil disponível no servidor durante a chamada Ajax
Faço a chamada Ajax no JavaScript do componente <h5:drop> , mostrado na Listagem 9, com função jsf.ajax.request() de JavaScript:
Listagem 9. O JavaScript do componente
<h5:drop> , Tomada II
if (!html5)
var html5 = {}
if (!html5.jsf) {
html5.jsf = {
init : function(ccid, payloadType, renderIds) {
var dropzone = $(ccid);
dropzone.payloadInput = $(ccid + ":form:payload");
dropzone.addEventListener("drop", function(event) {
if (payloadType == "")
payloadType = "text";
if (renderIds == "" || renderIds == "@this")
renderIds = ccid;
dropzone.payloadInput.value = event.dataTransfer
.getData(payloadType);
jsf.ajax.request(dropzone.payloadInput, event, {
render : renderIds,
onevent : function(data) {
if (data.status == "success")
html5.jsf.init(ccid, payloadType, renderIds);
}
});
}, false);
dropzone.addEventListener("dragenter", function(event) {
event.preventDefault();
}, false);
dropzone.addEventListener("dragover", function(event) {
event.preventDefault();
}, false);
}
};
} |
Faça rechamada da designação de carga útil na listagem 7:
<h5:drop...payload="#{dragDrop.payload}">
|
Torno a carga útil disponível através do componente entrada de texto invisível na seção <h5:drop> na A listagem 8.
Para transferir a carga útil para o cliente, preciso de uma maneira de mover uma válvula para trás e para frente entre o cliente e o servidor e o JSF já fornece isso com <h:inputText>. Assim, adiciono uma entrada de texto invisível no destino de descarte e, como pode ser visto no JavaScript na A listagem 9, defini o valor da entrada invisível quando um descarte é feito, logo antes da solicitação Ajax.
Como configurei o valor da entrada antes de fazer a chamada Ajax, a carga útil de arrastar e soltar, agora armazenada no valor da entrada, estará disponível no servidor durante a chamada Ajax. Como o texto de entrada está conectado a uma propriedade de bean, JSF passará a carga útil para esse método setter da propriedade durante a chamada Ajax.
Esse valor é usado pelo componente <h5:drop> na Listagem 8 como o valor da entrada de texto invisível:
<h:inputText id="payload" value="#{cc.attrs.carga útil}" style="display: none"/>
|
Em A listagem 9, a entrada de texto invisível é especificada como a origem da solicitação Ajax:
jsf.ajax.request(dropzone.payloadInput, event, {...});
|
Como a entrada de texto invisível é a origem da solicitação Ajax, JSF processa a entrada no servidor, o que significa que chamará as entradas de texto associadas ao método setter da propriedade — neste caso, DragDrop.setPayload(), mostrado na Listagem 10:
Listagem 10. O
carga útil implementação da propriedade
package com.clarity;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.StringTokenizer;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.gnu.stealthp.rsslib.RSSItem;
@Named @SessionScoped
public class DragDrop implements Serializable {
@Inject private RSSFeed rssFeed;
public DragDrop() {
}
public String getPayload() {
// JSF requer getters e setters para retorno das propriedades de entrada "";
}
public void setPayload(String payload) {
// cria um novo item salvo, baseado na carga útil. A carga útil
// foi configurada no listener do evento de descarte do componente h5:drop
// em /sections/feeds/menuLeft.xhtml
StringTokenizer st = new StringTokenizer(payload);
RSSItem item = new RSSItem();
item.setTitle(st.nextToken("|"));
st.nextToken(" ");
item.setLink(st.nextToken(" "));
rssFeed.getSavedItems().add(item);
}
} |
JSF passa a carga útil de arrastar e soltar para DragDrop.setPayload(). Com base nessa carga útil, o método DragDrop.setPayload() cria um novo item RSS e o inclui na lista de itens salvos do rssFeed. Observe que também incluo o método getter para a propriedade da carga útil, porque JSF requer setter e getter para os valores de entrada.
É fácil reutilizar a funcionalidade do componente existente — neste caso, a capacidade de <h:inputText>de transferir dados entre cliente e servidor — com componentes compostos. Utilizo a entrada de texto invisível no componente <h5:drop> para transferir dados do cliente para o servidor, simplesmente incluindo a entrada de texto no componente composto e configurando o valor da entrada antes de fazer a chamada Ajax.
Agora tenho um destino de descarte bastante sofisticado (observe que a origem do arrasto não mudou desde a sua introdução) que faz chamadas Ajax em resposta a descartes e transporta os dados transferidos do cliente para o servidor. Ele também permite que os autores de páginas especifiquem os componentes que desejam renderizar quando a chamada Ajax retornar. Mas os componentes de arrastar e soltar ainda têm um recurso ausente: arrasto condicional.
Na listagem 1 implementei a lista de links salvos no aplicativo Feeds como uma lista vinculada, o que significa que os usuários podem incluir duplicatas do mesmo artigo. Desejo desaprovar esse comportamento, como mostrado na Figura 7. Observe que o cursor no título arrastado não vira um sinal de + como faz no descarte bem-sucedido na Figura 2.
Figura 7. Desaprovando um descarte (o cursor não indica cópia)
Para desaprovar descartes em duplicata, altero os manipuladores de eventos drag-enter e drag-over do componente <h5:drop> para aceitar descartes condicionalmente, como mostrado na Listagem 11:
Listagem 11. JavaScript do componente
<h5:drop> , Tomada III
if (!html5)
var html5 = {}
if (!html5.jsf) {
html5.jsf = {
init : function(ccid, payloadType, renderIds) {
var dropzone = $(ccid);
if (dropzone.serverPayload) // already initialized
return;
dropzone.payloadInput = $(ccid + ":form:payload");
dropzone.acceptDrop = false;
dropzone.serverPayload = function() {
return dropzone.payloadInput.value;
};
dropzone.addEventListener("drop", function(event) {
if (payloadType == "")
payloadType = "text";
if (renderIds == "" || renderIds == "@this")
renderIds = ccid;
dropzone.payloadInput.value = event.dataTransfer
.getData(payloadType);
jsf.ajax.request(dropzone.payloadInput, event, {
render: renderIds
onevent : function(data) {
if (data.status == "success")
html5.jsf.init(ccid, payloadType, renderIds);
}
});
}, false);
dropzone.addEventListener("dragenter", function(event) {
if (dropzone.acceptDrop)
event.preventDefault();
}, false);
dropzone.addEventListener("dragover", function(event) {
if (dropzone.acceptDrop)
event.preventDefault();
}, false);
}
};
} |
Em Adicionando Ajax e enviando uma carga útil para o servidor, foquei a transferência da carga útil de arrastar e soltar do cliente para o servidor. Para aceitar descartes condicionalmente, devo executar o oposto: enviar informações do servidor de volta para o cliente. Nesse caso, preciso acesso à lista de links salvos, assim posso determinar se um link é uma duplicata. Assim, para transferir os dados de requisito do servidor de volta para o cliente, incluo uma variável serverPayload à dropzone.
Para enviar a lista de links salvos de volta para o servidor, armazeno uma cadeia de caractere que contém cada título de link salvo, na entrada de texto invisível do destino de descarte, implementando DragDrop.getPayload(), como mostrado na Listagem 12:
Listagem 12. O
carga útil método getter da propriedadepackage com.clarity; // importações omitidas. Consulte a seção A listagem 10. @Named @SessionScoped public class DragDrop implements Serializable { @Inject private RSSFeed rssFeed; public DragDrop() { } public String getPayload() { // envia uma cadeia de caractere que é uma concatenação dos // títulos dos itens salvos, de volta para o cliente LinkedList<RSSItem> savedItems = rssFeed.getSavedItems(); Iterator<RSSItem> it = savedItems.iterator(); String s = ""; while (it.hasNext()) { RSSItem = it.next(); s += item.getTitle =() + " | "; } return s; } public void setPayload(String payload) { // cria um novo item salvo, baseado na carga útil. A carga útil // foi configurada no listener de eventos do componente h5:drop // em /sections/feeds/menuLeft.xhtml StringTokenizer st = new StringTokenizer(payload); RSSItem item = new RSSItem(); item.setTitle(st.nextToken("|")); st.nextToken(" "); item.setLink(st.nextToken(" ")); rssFeed.getSavedItems().add(item); } } |
Na próxima vez que um arrasto for iniciado, a origem de arrasto do aplicativo verifica a carga útil do servidor (que é, naturalmente, o valor de entrada do texto invisível) para ver se o link arrastado é uma duplicata, como mostrado na Listagem 13:
Listagem 13. A fonte de arrasto, Tomada II
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h5="http://java.sun.com/jsf/composite/html5"
xmlns:places="http://java.sun.com/jsf/composite/places">
<script>
// event.target é um dos elementos h5:drag gerados por ui:repeat abaixo da
função dragStart(event) {
var linkref = event.target.firstElementChild.firstElementChild; // anchor
var link = linkref.href;
var title = linkref.textContent;
var dropzone = $("dropzone"); // h5:drop in dropZone.xhtml
dropzone.acceptDrop = true;
var serverPayload = dropzone.serverPayload();
if (serverPayload.indexOf(title) != -1)
dropzone.acceptDrop = false; // link already present
event.dataTransfer.setData('text', title + " | " + link + " ");
}
</script>
<h:panelGrid columns="1" id="items">
<ui:repeat value="#{rssFeed.items}" var="item">
<h5:drag ondragstart="dragStart(event)">
<p>
<a href="#{item.link}">#{item.title}</a> <br />
</p>
</h5:drag>
</ui:repeat>
</h:panelGrid>
</ui:composition> |
Se o link arrastado for uma duplicata, o JavaScript da origem de arrasto define o atributo acceptDrop do destino de descarte para false e o descarte é cancelado subsequentemente pelo destino de descarte.
Neste artigo mostrei como implementar componentes compostos com JSF 2 que encapsulam arrastar e soltar de HTML5. Os componentes compostos constituem uma ótima adição ao conjunto de ferramentas do desenvolvedor de JSF porque permitem implementar componentes reutilizáveis sem precisar escrever código Java ou executar qualquer configuração. Também ilustrei como reutilizar componentes existentes, como o elemento <h:inputText> incorporado do JSF para reduzir a quantia de trabalho necessária que deve ser feita ao implementar seus próprios componentes compostos.
Após ter desenvolvido um punhado de componentes compostos, faz sentido agrupá-los em um arquivo JAR para que outros desenvolvedores possam utilizá-los. Basta ativar o JAR, com um diretório META-INF no lugar de WEB-INF.
Na próxima parte do JSF2 fu darei uma nova olhada no que cobrimos na série e recomendarei algumas boas práticas de JSF 2.
| Descrição | Nome | Tamanho | Método de download |
|---|---|---|---|
| Sample code for this article | j-jsf2fu-1110.zip | 5.39MB | HTTP |
Informações sobre métodos de download
Aprender
- O Web site JSF: Localize mais recursos sobre desenvolvimento com JSF.
- Exploring HTML5 with JavaServer Faces 2.0: Confira essa apresentação de slides de Roger Kitain, o líder de coespecificação do JSF.
- The HTML5 Specification: The official specification for the HTML5 standard — ainda em andamento em novembro de 2010 — está aqui. Explore o Arrastar e Soltar anterior.
-
" =Native HTML5 Drag and Drop" (Eric Bidelman, HTML5Rocks, setembro 2010): Saiba mais sobre arrastar e soltar do HTML5.
- HTML5 tag reference: W3schools documents HTML5's elements.
- HTML5 Tutorial: Esse site publica numerosos tutoriais de HTML5, incluindo esse sobre arrastar e soltar.
- DOM de JavaScript: documentação semelhante a Javadoc do DOM de Javascript.
-
"XHTML5 in a nutshell" (Sergey Mavrody, The WHATWG Blog, julho de 2010): Leia sobre o HTML5 poliglota.
-
zona de tecnologia Java do developerWorks: Encontre centenas de artigos sobre cada aspecto da programação Java.
Obter produtos e tecnologias
Discutir
- Participe do comunidade do My developerWorks. Entre em contato com outros usuários do developerWorks e explore os blogs, fóruns, grupos e wikis voltados para desenvolvedores.

O autor, palestrante e consultor David Geary é o presidente da Clarity Training, Inc. onde ensina desenvolvedores a implementarem aplicativos da Web usando JSF e Google Web Toolkit (GWT). Ele fez parte dos Grupos de Especialistas do JSTL 1.0 e do JSF 1.0/2.0, foi coautor do Exame de Certificação de Desenvolvedor da Web da Sun e contribuiu para projetos de software livre, incluindo o Apache Struts e o Apache Shale. Graphic Java Swing de David foi um dos livros de Java mais vendidos de todos os tempos e Core JSF (coescrito com Cay Horstman) é o livro de JSF mais vendido. David fala frequentemente em conferências e para grupos de usuários. É frequentador assíduo do tour de NFJS desde 2003, deu cursos na Java University e foi duas vezes votado como JavaOne rock star.