No artigo anterior do JSF 2 fu, eu discuti a implementação de um componente composto de preenchimento automático com o Ajax integrado. Os autores de páginas usam o componente em um facelet e o componente cuida de todos os detalhes do Ajax. O Ajax integrado possui seu lugar, mas também é conveniente permitir aos autores de páginas a inclusão de Ajax no componente composto depois (talvez bem depois) de o desenvolvedor ter implementado o componente. Este artigo mostra como os seus componentes compostos podem acomodar o Ajax de última hora.
Conforme discutido no artigo "JSF 2 fu, Parte 3: Manipulação de Eventos, JavaScript e Ajax", a tag <f:ajax> do JSF 2 permite aos autores de páginas a inclusão de Ajax de última hora aos componentes integrados do JSF 2. Por exemplo: com a <f:ajax> é possível facilmente transformar um botão de envio em um botão do Ajax:
<h:commandButton value="Click me"> <f:ajax> </h:commandButton> |
No entanto, a tag <f:ajax> do JSF 2 não funciona — sem uma pequena ajuda — com componentes compostos, pois os compostos são simplesmente contêineres de componentes.
Por exemplo: no artigo "JSF 2 fu, Parte 2: Uso de modelos e componentes compostos", eu introduzi um componente composto de ícone simples que consiste em um link, representado por uma imagem. Quando o usuário clique no ícone, o ícone envia o formulário circundante, que aciona um listener de ação do lado do servidor associado ao link do ícone. O uso de ícones é simples:
<util:icon image="..."> <f:actionListener for="link" type="..."> </util:icon> |
Como é possível transformar botões de envio em botões de AjaComx com a tag <f:ajax> , você pode pensar que é possível fazer a mesma coisa para ícones:
<util:icon image="..."> <f:ajax> <f:actionListener for="link" type="..."> </util:icon> |
O fragmento de código anterior não funciona, pois estou anexando a tag <f:ajax> no componente de ícone, quando o que realmente desejo fazer é anexá-la ao link dentro do ícone.
O necessário neste exemplo é um mecanismo que permita a conexão do comportamento do Ajax ao link dentro do ícone, ou de forma mais geral, que permita a conexão do componente do Ajax aos componentes que residem nos componentes compostos. Esse mecanismo — implementados no Mojarra e Apache MyFaces e que não possuem documentação no JSF 2.0 — é o foco deste artigo. (Observação: O suporte ao MyFaces foi incluído enquanto este artigo estava sendo escrito.) Antes de ver como o mecanismo funciona, irei criar um novo componente composto de ícone para uso com o mecanismo.
Um componente de ícone reutilizável
Imagine que você tem a profissão mais legal do mundo. Talvez você esteja implementado o mecanismo de gráfico World of Warcraft de última geração. Mas infelizmente não hoje. Hoje você implementará o seletor de fonte mostrado na Figura 1:
Figura 1. Selecionando uma fonte
Seus chefes perguntam quanto tempo vai demorar essa implementação. Eles querem os ícones de + e - que os usuários podem clicar para alterar o tamanho da fonte na visualização com dois caracteres. Obviamente eles querem o uso de Ajax para que a visualização e a exibição do tamanho próximas aos ícones atualizem dinamicamente sem interferir no resto da página.
Os seus chefes esperam um componente simples de seleção de fonte, mas você fará melhor. Você implementará um componente de ícone de uso geral que pode ser configurado no tempo de execução com uma imagem e uma ação, totalmente preparado para Ajax, e usará o componente de ícone na visualização da fonte. Dessa forma, você terá um componente de ícone útil que será muito conveniente no futuro.
Agora irei mostrar como implementar o componente de ícone em menos de 25 linhas de XML.
O exemplo de seletor de fonte consiste em quatro arquivos:
- A página mostrada na Figura 1, definida no index.xhtml.
- O componente de ícone, que reside em /resources/util/icon.xhtml.
- Um listener (com.clarity.FontSelectionListener.java).
- Um bean (com.clarity.FontSettings).
A Figura 2 mostra a estrutura de diretório:
Figura 2. Os arquivos do exemplo de seletor de fonte
A Listagem 1 é o facelet — index.xhtml — da página mostrada na Figura 1:
Listagem 1. O facelet (/web/index.xhtml)
<?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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:corejsf="http://corejsf.com"
xmlns:util="http://java.sun.com/jsf/composite/util">
<h:head>
<h:outputStylesheet library="css" name="styles.css"/>
<title>#{msgs.windowTitle}</title>
</h:head>
<h:body>
<h:outputStylesheet library="css" name="styles.css"/>
<h:outputText value="#{msgs.fontSizeHeading}"
style="padding-left: 30px; font-size: 2em;"/>
<h:panelGrid columns="3" style="padding-left: 80px;">
<util:icon id="minus" image="#{resource['images:minus.gif']}">
<f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
</util:icon>
<util:icon id="plus" image="#{resource['images:plus.gif']}">
<f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
</util:icon>
<h:outputText id="readout" value="#{fontSettings.size}em"/>
</h:panelGrid>
<h:outputText id="fontPreview" value="Aa"
style="font-size: #{fontSettings.size}em; font-style: italic"/>
</h:body>
</html>
|
O facelet na Listagem 1 declara um namespace para o componente de ícone e usa o componente na página. Esse é o uso de componentes compostos 101 do JSF 2.0, abrangido em detalhes no artigo "JSF 2 fu, Parte 2: Uso de modelos e componentes compostos."
Observe que os dois ícones são equipados com um listener de ação para o componente do link do ícone. Quando um usuário clica no link do ícone, o JSF invoca esse listener, mostrado na Listagem 2, no servidor.
Listagem 2. O listener (com/clarity/FontSelectionListener.java)
package com.clarity;
import javax.el.ELResolver;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.faces.event.ActionListener;
public class FontSelectionListener implements ActionListener {
@Override
public void processAction(ActionEvent event)
throws AbortProcessingException {
FacesContext c = FacesContext.getCurrentInstance();
ELResolver elResolver = c.getApplication().getELResolver();
FontSettings fs = (FontSettings)
elResolver.getValue(c.getELContext(), null, "fontSettings");
if (((UIComponent)event.getSource()).getClientId().startsWith("minus"))
fs.decrement();
else
fs.increment();
}
}
|
Na Listagem 2, olhei para ver se o identificador de cliente do componente que acionou o evento é minus; se for, sei que o usuário clicou no ícone do sinal de menos, e eu reduzo o tamanho da fonte. Caso contrário, eu aumento o tamanho da fonte.
Observe que o listener obtém uma referência para o bean gerenciado fontSettings . Ele faz isso obtendo uma referência para o resolvedor de idioma de expressão, que sabe como localizar beans gerenciados de acordo com seus nomes. O bean fontSettings é mostrado na Listagem 3:
Listagem 3. O bean de configuração de fonte (com/clarity/FontSettings.java)
package com.clarity;
import java.io.Serializable;
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
@Named
@SessionScoped
public class FontSettings implements Serializable {
private static int INCREMENT = 1;
private int size = 1;
public int getSize() { return size; }
public void setSize(int newValue) { size = newValue; }
public void increment() { size += INCREMENT; }
public void decrement() { size -= INCREMENT; }
}
|
As três listagens anteriores mostram todo o código no aplicativo, com exceção do componente composto de ícone. Ele é o item seguinte.
Implementando o componente composto de ícone
Os ícones possuem três requisitos:
- A imagem deve ser configurável.
- A ação quando o usuário clica na imagem deve ser configurável.
- O ícone deve oferecer suporte para Ajax.
Na Listagem 4, eu cumpro os dois primeiros requerimentos:
Listagem 4. O componente composto
<util:icon> , parte 1 (/resources/util/icon.xhtml)
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="image" required="true"/>
<composite:actionSource name="link" targets="#{cc.clientId}:iconForm:link"/>
</composite:interface>
<composite:implementation>
<div id="#{cc.clientId}">
<h:form id="iconForm">
<h:commandLink id="link" immediate="true">
<h:graphicImage value="#{cc.attrs.image}"/>
</h:commandLink>
</h:form>
</div>
</composite:implementation>
</html>
|
O componente de ícone na Listagem 4 declara um atributo de imagem e um actionSource chamado link.
Esse actionSource é usado na Listagem 1 como o valor do atributo for do <f:actionListener> . Se não ficar completamente claro, será possível ler mais sobre como as fontes de ação funcionam nos componentes compostos no artigo "JSF 2 fu, Parte 2: Uso de modelos e componentes compostos", que discute brevemente uma implementação de ícone similar à implementação da Listagem 4.
A implementação do componente de ícone na Listagem 4 permite ao autor de páginas a configuração da aparência e do comportamento dos ícones, mas não permite ao autor a conexão de comportamentos de Ajax ao componente. De acordo com a implementação atual, se o usuário clicar em uma imagem, o JSF envia a página completa e redesenha completamente a página ao retornar.
Agora você verá como permitir aos autores de páginas a inclusão de Ajax ao componente de ícone.
Incluindo suporte para Ajax com <composite:clientBehavior>
Para permitir aos autores de páginas a inclusão de Ajax no link presente no componente <util:icon> , eu uso a tag <composite:clientBehavior> , conforme mostrado na Listagem 5:
Listagem 5. O componente composto
<util:icon>, parte 2
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface>
<composite:attribute name="image" required="true"/>
<composite:actionSource name="link" targets="#{cc.clientId}:iconForm:link"/>
<composite:clientBehavior name="click"
event="action"
targets="#{cc.clientId}:iconForm:link"/>
</composite:interface>
<composite:implementation>
<div id="#{cc.clientId}">
<h:form id="iconForm">
<h:commandLink id="link" immediate="true">
<h:graphicImage value="#{cc.attrs.image}"/>
</h:commandLink>
</h:form>
</div>
</composite:implementation>
</html>
|
A tag <composite:clientBehavior> expõe um evento de Ajax para um componente contido em um componente composto. Na Listagem 5, eu declaro um comportamento de cliente com o nome lógico click associado ao evento real — o evento action disparado pelo link do ícone. Aqui está um resumo dos atributos válidos da tag <composite:clientBehavior> :
name: O nome do evento usado pelos autores de páginas.default: Pode sertrueoufalse. Se fortrue, o evento especificado no atributo name será o evento padrão.event: O nome real do evento.targets: Uma lista separada por espaço dos IDs de componentes para os quais o JSF redireciona o comportamento.
Agora os autores de páginas podem conectar os comportamentos de Ajax ao componente de ícone, conforme mostrado na Listagem 6:
Listagem 6. O facelet, parte 2
<?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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:corejsf="http://corejsf.com"
xmlns:util="http://java.sun.com/jsf/composite/util">
<h:head>
<h:outputStylesheet library="css" name="styles.css"/>
<title>#{msgs.windowTitle}</title>
</h:head>
<h:body>
<h:outputStylesheet library="css" name="styles.css"/>
<h:outputText value="#{msgs.fontSizeHeading}"
style="padding-left: 30px; font-size: 2em;"/>
<h:panelGrid columns="3" style="padding-left: 80px;">
<util:icon id="minus" image="#{resource['images:minus.gif']}">
<f:ajax event="click" render=":readout :fontPreview"/>
<f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
</util:icon>
<util:icon id="plus" image="#{resource['images:plus.gif']}">
<f:ajax event="click" render=":readout :fontPreview"/>
<f:actionListener for="link" type="com.clarity.FontSelectionListener"/>
</util:icon>
<h:outputText id="readout" value="#{fontSettings.size}em"/>
</h:panelGrid>
<h:outputText id="fontPreview" value="Aa"
style="font-size: #{fontSettings.size}em; font-style: italic"/>
</h:body>
</html>
|
Na Listagem 6, eu incluí uma tag <f:ajax> nos ícones. Quando o usuário clica em um dos ícones, o JSF cria uma chamada de Ajax para o servidor, e quando a chamada retorna, o JSF renderiza os componentes readout e fontPreview .
Se preferir, será possível incluir um atributo default="true" na tag <composite:clientBehavior> na interface do ícone. Isso elimina a necessidade de especificação do evento click pelo autor de página, reduzindo as tags <f:ajax> na Listagem 6 para <f:ajax render=":readout :fontPreview">.
Neste artigo, mostrei como o JSF 2 facilita, mesmo com uma tag não documentada, a inclusão de funcionalidades do Ajax nos componentes compostos pelos autores de páginas. Realmente, conforme visto, é possível fornecer aos autores de páginas a capacidade de transformação de componentes compostos que fazem solicitação de HTTP completas em componentes que fazem solicitações Ajax. E tudo isso é o resultado de uma única linha de XML em uma interface do componente composto.
No próximo artigo do JSF 2 fu, vou deixar os componentes compostos um pouco de lado para abordar o uso dos Contextos e Injeção de Dependência (CDI) com o JSF 2.
| Descrição | Nome | Tamanho | Método de download |
|---|---|---|---|
| Sample code for this article | j-jsf2fu-0610-src.zip | 11KB | HTTP |
Informações sobre métodos de download
Aprender
- A página inicial do JSF: Localize mais recursos sobre desenvolvimento com o JSF.
- Apache MyFaces: O projeto MyFaces fornece uma implementação do JSF que oferece suporte para a tag
<composite:clientBehavior>. - Blog do Roger Kitain: Roger Kitain e Ed Burns são os líderes colaborativos da especificação do JSF 2.0.
-
Blog do Jim Driscoll: Possui diversas entradas relacionadas ao JSF 2.
-
Blog do Ryan Lubke: Ryan Lubke trabalha na implementação de referência do JSF 2.
- O erro de especificação do
<composite:clientBehavior>: Leia sobre o erro de especificação relacionado à exclusão do<composite:clientBehavior>da documentação do JSF 2.0. -
Zona de tecnologia Java do developerWorks: Encontre centenas de artigos sobre cada aspecto da programação Java.
Obter produtos e tecnologias
- JSF: Faça o download do JSF 2.0.
Discutir
-
"Public Access to JSF 2.0 JSR-314-EG Discussions Now Available" (Blog do Ed Burns, java.net, março de 2009): Descubra como se inscrever na lista de e-mails do Grupo de Especialistas em JSF 2.
- Participe da comunidade My developerWorks.

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.