JSF 2 fu, Parte 2: Uso de modelos e componentes compostos

Implementar Uls expansíveis com JavaServer Faces 2

Java™Server Faces (JSF) 2 permite implementar interfaces de usuário fáceis de modificar e ampliar com dois recursos poderosos: uso de modelos e componentes compostos. Neste artigo — segundo de uma série de três partes sobre novos recursos de JSF 2 — o membro do Grupo de Especialistas em JSF 2, David Geary, nos mostra como suas aplicações Web podem aproveitar da melhor maneira o uso de modelos e componentes compostos.

David Geary, President, Clarity Training, Inc.

David GearyO autor, orador e consultor David Geary é presidente da Clarity Training, Inc., onde ensina desenvolvedores a implementar aplicações Web usando JSF e Google Web Toolkit (GWT). Ele participou dos Grupos de Especialistas em JSTL 1.0 e JSF 1.0/2.0, foi co-autor do Exame de Certificação de Desenvolvedor de Web da Sun e contribuiu em projetos de fonte aberta, incluindo Apache Struts e Apache Shale. O Graphic Java Swing de David foi um dos livros sobre Java mais vendidos de todos os tempos, e o Core JSF (co-escrito com Cay Horstman), é o livro sobre JSF mais vendido. David discursa regularmente em conferências e grupos de usuário. Ele comparece com freqüência ao tour do NFJS desde 2003, ministrou cursos na Universidade Java e foi eleito duas vezes rock star do JavaOne.



14/Mai/2014

De volta a 2000, quando era participante ativo em uma lista de e-mails de JavaServer Pages(JSP), me deparei com Craig McClanahan que estava trabalhando numa estrutura de Web que estava surgindo chamada Struts. Naquela época, enquanto mudava de programação Java Swing para server-side, tinha implementado uma pequena estrutura que separou um layout de visualização de JSP de seu conteúdo, parecido em essência com os gerenciadores de layout do Swing. Craig perguntou se eu gostaria de incluir minha biblioteca de modelos no Struts, e eu concordei imediatamente. A Biblioteca de Modelos do Struts, agrupada com Struts 1.0, tornou-se a base para a popular biblioteca de Tiles do Struts, que acabou se tornando uma estrutura Apache de alto nível.

Atualmente, a tecnologia de exibição padrão do JSF 2 — Facelets— é uma estrutura de modelos baseada em grande parte no Tiles. O JSF 2 também fornece um poderoso mecanismo chamado componentes compostos, que se acrescenta aos recursos de modelo do Facelets permitindo implementar componentes customizados sem código Java e sem configuração XML. Neste artigo, apresentarei o uso de modelos e componentes compostos, com três dicas para obter o máximo do JSF 2:

  • Dica 1: Mantenha-se DRY
  • Dica 2: Fique composto
  • Dica 3: Pense LEGOs

Facelets e JSF 2

Enquanto padronizava a implementação de Facelets de fonte aberta, o Grupo de Especialistas de JSF 2 realizou algumas mudanças na API subjacente do Facelets, porém mantiveram a compatibilidade com versões anteriores da biblioteca de tags. Isso significa que visualizações existentes implementadas com a versão de fonte aberta do Facelets deveriam funcionar com JSF 2.

Saiba mais sobre os muitos recursos do Facelets nos artigos de Rick Hightower "Facelets Fits JSF like a Glove" e "Advanced Facelets programming."

Dica 1: Mantenha-se DRY

Em meu primeiro trabalho como desenvolvedor de software, implementei uma GUI para sistemas de projeto auxiliado por computador e fabricação auxiliada por computador (CAD/CAM) baseada em UNIX®.

Tudo ia razoavelmente bem no início, porém ao longo do tempo, meu código se tornou cada vez mais problemático. Na ocasião em que efetuamos o release, o sistema era tão frágil que eu temia reparar erros, e o release precipitou um fluxo constante de relatórios de erro.

Se eu tivesse seguido o princípio de DRY — Don't Repeat Yourself (não se repita) — naquele projeto, eu poderia ter me poupado de bastante arrependimento. O princípio de DRY, criado por Dave Thomas e Andy Hunt (vide Recursos), afirma:

Cada parte do conhecimento deve ter uma representação única, não ambígua, oficial dentro de um sistema.

Minha aplicação CAD/CAM não era DRY — ela tinha muito acoplamento entre as preocupações — portanto, mudanças em uma área causavam mudanças inesperadas em outro lugar.

O JSF 1 violou o princípio DRY em diversos aspectos, por exemplo, lhe forçando a fornecer duas representações de seus beans gerenciados — uma em XML, e outro em código Java. A necessidade de representações múltiplas tornou mais difícil criar e mudar beans gerenciados. Conforme lhe mostrei na Parte 1, o JSF 2 permite utilizar anotações em vez de XML para configurar beans gerenciados, fornecendo uma representação única e oficial de seus beans gerenciados.

Beans gerenciados à parte, mesmo práticas que podem parecer benignas — como, por exemplo, incluir na mesma folha de estilo em todas as suas visualizações — violam o princípio DRY e podem causar consternação. Se o nome da folha de estilo for alterado, você deve alterar visualizações múltiplas. Se for possível, é melhor encapsular a inclusão de folha de estilo.

O princípio DRY também se aplica ao seu design de código. Se você tiver métodos múltiplos em que todos contenham código para pesquisar uma árvore, por exemplo, uma boa idéia é encapsular o algoritmo pesquisa de árvore, talvez em uma subclasse.

É especialmente importante manter-se DRY ao implementar uma Interface de Usuário onde (discutivelmente) a maioria das mudanças ocorre durante o desenvolvimento.

O uso de modelos do JSF 2

Um dos muitos modos nos quais o JSF 2 suporta o princípio DRY é o uso de modelos. Modelos encapsulam funcionalidades que são comuns entre visualizações em sua aplicação, portanto, é preciso especificar essa funcionalidade somente uma vez. Um modelo é utilizado por composições múltiplas para criar visualizações em uma aplicação JSF 2.

A aplicação de locais, que introduzi na Parte 1, possui as três visualizações mostradas na Figura 1:

Figura 1. As visualizações de aplicação de locais: Login, visualizador de fonte, e locais

Como muitas aplicações de Web, a aplicação de locais contém visualizações múltiplas que compartilham o mesmo layout. O uso de modelos JSF permite encapsular esse layout — juntamente com outros artefatos compartilhados, como por exemplo, JavaScript e Folhas de Estilo em Cascata (CSS) — em um modelo. A listagem 1 é o modelo para as três visualizações mostradas na Figura 1:

Listagem 1. O modelo de locais: /templates/masterLayout.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:ui="http://java.sun.com/jsf/facelets">    
 <h:head>     
 <title>       
 <ui:insert name="windowTitle">        
 #{msgs.placesWindowTitle}      
 </ui:insert>     
 </title>    
 </h:head>      
 <h:body>       
 <h:outputScript library="javascript" 
 name="util.js" target="head"/>           
 <h:outputStylesheet library="css" name="styles.css" 
 target="body"/>	              
 <div class="pageHeading">      
 <ui:insert name="heading">        
 #{msgs.placesHeading}      
 </ui:insert>          
 </div>            
 <div class="menuAndContent">        
 <div class="menuLeft">         
 <ui:insert name="menuLeft"/>      
 </div>     	           
 <div class="content" style="display: #{places.showContent}">       
 <ui:insert name="content"/>      
 </div>  	          
 <div class="menuRight">         
 <ui:insert name="menuRight">          
 <ui:include src="/sections/shared/sourceViewer.xhtml"/>         
 </ui:insert>       
 </div>      
 </div>     
 </h:body>  
 </html>

O modelo na Listagem 1 fornece a seguinte infra-estrutura para todas as visualizações de aplicação:

  • HTML <head>, <body>, e <title>
  • Um título padrão (que pode ser sobreposto por composições que usam o modelo)
  • Uma folha de estilo CSS
  • Algum utilitário JavaScript
  • Um layout na forma de <div>s e suas respectivas classes CSS
  • Conteúdo padrão para o cabeçalho (que pode ser sobreposto)
  • Conteúdo padrão para o menu direito (que pode ser sobreposto)

Conforme ilustra a Listagem 1, os modelos inserem conteúdo em seu layout com a tag <ui:insert>.

Se for especificado um corpo para uma tag <ui:insert>, como fiz com o título da janela, cabeçalho e menu direito na Listagem 1, o JSF usa o corpo da tag como conteúdo padrão. Composições que utilizam o modelo podem definir conteúdo, ou desativar o conteúdo padrão, com a tag <ui:define>, conforme comprovado pela Listagem 2, que mostra a marcação para a visualização de login:

Listagem 2. A visualização de login
 <ui:composition xmlns="http://www.w3.org/1999/xhtml"    
 xmlns:ui="http://java.sun.com/jsf/facelets"   
 template="/templates/masterLayout.xhtml">       
 <ui:define name="menuLeft">     
 <ui:include src="/sections/login/menuLeft.xhtml"/>   
 </ui:define>    
 <ui:define name="content">      
 <ui:include src="/sections/login/content.xhtml"/>              
 </ui:define>       
 </ui:composition>

A visualização de login utiliza o conteúdo padrão de modelo para o título da janela, cabeçalho e menu direito. Ela define somente as funcionalidades que são especificas para a visualização de login: a seção de conteúdo e o menu esquerdo.

Através do fornecimento de uma tag <ui:define> para o título da janela, cabeçalho, ou menu direito, pude sobrepor o conteúdo padrão definido pelo modelo. Por exemplo, a Listagem 3 apresenta a visualização de visualizador fonte (a imagem central na Figura 1):

Listagem 3. A visualização do visualizador fonte
 <ui:composition xmlns="http://www.w3.org/1999/xhtml"    
 xmlns:ui="http://java.sun.com/jsf/facelets"    
 template="/templates/masterLayout.xhtml">    
 <ui:define name="content">     
 <ui:include src="/sections/showSource/content.xhtml"/>   
 </ui:define>    
 <ui:define name="menuLeft">     
 <ui:include src="/sections/showSource/menuLeft.xhtml"/>         
 </ui:define>         
 <ui:define name="menuRight">    
 <ui:include src="/sections/showSource/menuRight.xhtml"/>         
 </ui:define>  </ui:composition>

A visualização do visualizador fonte define o conteúdo para a seção de conteúdo e para o menu direito. Ela também se sobrepõe ao conteúdo padrão, definido pelo modelo na Listagem 1, para o menu esquerdo.

A Listagem 4 apresenta a visualização de locais (a imagem inferior na Figura 1):

Listagem 4. A visualização de locais
 <ui:composition xmlns="http://www.w3.org/1999/xhtml"   
 xmlns:ui="http://java.sun.com/jsf/facelets"   
 template="/templates/masterLayout.xhtml">    
 <ui:define name="menuLeft">     
 <ui:include src="/sections/places/menuLeft.xhtml"/>    
 </ui:define>   
 <ui:define name="content">    
 <ui:include src="/sections/places/content.xhtml"/>  
 </ui:define>  </ui:composition>

O uso de modelos do JSF 2

O conceito por trás do uso de modelos é simples. É possivel definir um único modelo que encapsula funcionalidades comuns entre visualizações múltiplas. Cada visualização consiste em uma composição e um modelo.

Quando o JSF cria uma visualização, ele carrega o modelo de composição e, então, insere o conteúdo definido pela composição no modelo.

Observe as similaridades entre as Listagens 2, 3, e 4. Todas as três visualizações especificam seu modelo e definem conteúdo. Observe, também, como é fácil criar novas visualizações, pois a maioria da infra-estrutura é encapsulada em um modelo e arquivos incluídos.

Outro aspecto interessante do uso de modelo de JSF é que as visualizações como aquelas nas Listagens 2, 3 e 4 não mudam muito ao longo do tempo, portanto, uma boa parte de seu código de visualização, essencialmente, não requer manutenção.

Como as visualizações que os utilizam, os modelos também tendem a mudar muito pouco. E, pelo fato de você ter encapsulado tantas funcionalidades comuns em código quase livre de manutenção, é possível concentrar-se no conteúdo real de suas visualizações — que vai no menu esquerdo da página de login, por exemplo. Concentração em seu conteúdo real de visualização é o assunto da próxima dica.


Dica 2: Fique composto

Não muito tempo depois que minha GUI de CAD/CAM foi lançada, passei alguns meses trabalhando em um projeto à parte com um desenvolvedor chamado Bob. Trabalhávamos em sua base de código, e surpreendentemente, conseguíamos realizar mudanças e reparar erros com facilidade.

Logo percebi que a única maior diferença entre o código de Bob e o meu era que ele escreveu métodos minúsculos— tipicamente entre 5 e 15 linhas de código — e seu sistema inteiro estava unido a partir desses métodos. Enquanto eu tinha lutado para modificar longos métodos com muitas preocupações em meu projeto anterior, Bob, ligeiramente, combinou pequenos métodos com funcionalidade atômica. A diferença na manutenção e expansibilidade entre o código de Bob e o meu era como o dia e a noite, e, a partir de então, passei a utilizar métodos pequenos.

Nem Bob nem eu, sabíamos na época, mas estávamos utilizando um padrão de design da Smaltalk chamado Método Composto (vide Recursos):

Divida seu software em métodos que realizem uma tarefa identificável, em um único nível de abstração.

Os benefícios de utilizar o padrão de Método Composto estão bem documentados. (Vide "Evolutionary architecture and emergent design: Composed method and SLAP" para uma excelente explicação detalhada.) Aqui, focarei em como é possível utilizar o padrão de Método Composto com suas visualizações de JSF.

O JSF 2 lhe incentiva a compor suas visualizações a partir de fragmentos de visualização menores. O uso de modelo encapsula funcionalidades comuns, desse modo, dividindo suas visualizações em porções menores. O JSF 2 também fornece uma tag <ui:include>, conforme demonstrei nas listagens anteriores, que ainda lhe permite dividir suas visualizações em funcionalidades menores. Por exemplo, a Figura 2 apresenta o menu esquerdo da página de login de aplicação de locais:

Figura 2. O menu esquerdo da página de login

E a Listagem 5 apresenta o arquivo que define o conteúdo do menu:

Listagem 5. A implementação do menu esquerdo da visualização de login
 <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">    
 <div class="menuLeftText">     
 #{msgs.welcomeGreeting}      
 <div class="welcomeImage">      
 <h:graphicImage library="images" name="cloudy.gif"/>    
 </div>   </div>     
 </html>

A marcação na Listagem 5 é simples, o que facilita a leitura, compreensão, manutenção e ampliação do arquivo. Se o mesmo código fosse inserido em uma única e longa página XHTML que contivesse todo o necessário para implementar a visualização de login, alterar isso seria incômodo.

A Figura 3 apresenta o menu esquerdo de visualização de locais:

Figura 3. O menu esquerdo da visualização de locais

A Listagem 6 apresenta uma implementação do menu esquerdo de visualização de locais:

Listagem 6. A implementação do menu esquerdo de visualização de locais
 <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:util="http://java.sun.com/jsf/composite/components/util">    
 <div class="placesSearchForm">     
 <div class="placesSearchFormHeading">      
 #{msgs.findAPlace}     
 </div>     	     
 <h:form prependId="false">       
 <h:panelGrid columns="2"> 	            
 #{msgs.streetAddress}         
 <h:inputText value="#{place.streetAddress}" size="15"/> 	             
 #{msgs.city}  
 <h:inputText value="#{place.city}"  
 size="10"/>         
 #{msgs.state} 
 <h:inputText value="#{place.state}" size="3"/>         
 #{msgs.zip}   
 <h:inputText value="#{place.zip}"   
 size="5"/>   	              
 <h:commandButton value="#{msgs.goButtonText}"           
 style="font-family:Palatino;font-style:italic"           
 action="#{place.fetch}"/> 	       	                  
 </h:panelGrid>	             
 </h:form>   
 </div> 	   
 <util:icon image="#{resource['images:back.jpg']}"    
 actionMethod="#{places.logout}"     
 style="border: thin solid lightBlue"/>  
 </ui:composition> 
g

A Listagem 6 implementa um formulário e utiliza um componente de ícone. (Discutirei esse componente de ícone em O componente de ícone, brevemente. Por enquanto, é suficiente saber que um autor de página pode associar uma imagem e um método a um ícone.) A imagem para o ícone de logout é apresentada na parte inferior da Figura 3, e o método de ícone de logout — places.logout()— é apresentado na Listagem 7:

Listagem 7. O método Places.logout()
 package com.clarity; ... 
 @ManagedBean() @SessionScoped  
 public class Places 
 {   
 private ArrayList<Place> places = null;  
 ...     
 private static SelectItem[] zoomLevelItems = 
 {   ...   
 public String logout() 
 {     
 FacesContext fc = FacesContext.getCurrentInstance();          
 ELResolver elResolver = fc.getApplication().getELResolver(); 	          
 User user = (User)elResolver.getValue
 (       
 fc.getELContext(), null, "user"); 	    
 user.setName("");     
 user.setPassword(""); 	        
 setPlacesList(null);      
 return "login";    
 } 
 }

Na minha opinião, a Listagem 6 — a implementação do menu esquerdo de visualização de locais — se aproxima de um limite de excesso de código por volta de 30 linhas de marcação. Esta listagem é um tanto difícil de ler, e duas coisas nesse código poderiam ser refatoradas em seus próprios arquivos: o formulário e o ícone. A Listagem 8 apresenta a versão refatorada da Listagem 6 que encapsula o formulário e o ícone em seus próprios arquivos XHTML:

Figura 8. Refatorando o menu esquerdo de visualização de locais
 <ui:composition xmlns="http://www.w3.org/1999/xhtml"     
 xmlns:ui="http://java.sun.com/jsf/facelets">    
 <div class="placesSearchForm">      
 <div class="placesSearchFormHeading">       
 #{msgs.findAPlace}     
 </div>         
 <ui:include src="addressForm.xhtml">	   
 <ui:include src="logoutIcon.xhtml">	  
 </div>      
 </ui:composition>

A Listagem 9 apresenta addressForm.xhtml:

Listagem 9. addressForm.xhtml
 <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">        
 <h:form prependId="false">    
 <h:panelGrid columns="2">           
 #{msgs.streetAddress}       
 <h:inputText value="#{place.streetAddress}" size="15"/>              
 #{msgs.city}  
 <h:inputText value="#{place.city}"  
 size="10"/>      
 #{msgs.state} 
 <h:inputText value="#{place.state}" 
 size="3"/>       
 #{msgs.zip}   
 <h:inputText value="#{place.zip}"   
 size="5"/>              
 <h:commandButton value="#{msgs.goButtonText}"          
 style="font-family:Palatino;font-style:italic"         
 action="#{place.fetch}"/>                           
 </h:panelGrid>           
 </h:form>    
 </ui:composition>

Listagem 10 apresenta logoutIcon.xhtml:

Listagem 10. logoutIcon.xhtml
 <ui:composition xmlns="http://www.w3.org/1999/xhtml"     
 xmlns:ui="http://java.sun.com/jsf/facelets"     
 xmlns:util="http://java.sun.com/jsf/composite/components/util">    
 <util:icon image="#{resource['images:back.jpg']}"     
 actionMethod="#{places.logout}"     
 style="border: thin solid lightBlue"/>  
 </ui:composition>

Ao compor suas visualizações a partir de múltiplos arquivos pequenos, você colhe os benefícios do padrão Smalltal's Composed Method. É possível também organizar os arquivos para facilitar ainda mais a reação à mudança. Por exemplo, a Figura 4 mostra os arquivos que constituem as três visualizações na aplicação de locais:

Figura 4. As visualizações da aplicação de locais

Três diretórios que criei — views, sections e templates — contêm a maior parte dos arquivos XHTML usados para implementar as visualizações da aplicação de locais. Como os arquivos nos diretórios views e templates raramente mudam, posso me concentrar no diretório sections. Se quiser mudar o ícone no menu esquerdo da página de login, por exemplo, sei exatamente aonde ir: sections/login/menuLeft.xhtml.

Você pode usar qualquer estrutura de diretório que desejar para organizar seus arquivos XHTML. Uma estrutura lógica facilita a localização do código que você precisa modificar.

Além de aderir ao princípio DRY e usar o padrão de Método Composto, é também uma boa idéia encapsular a funcionalidade em componentes customizados. Os componentes são um poderoso mecanismo de reutilização, e você deve aproveitar esse poder. Ao contrário do JSF 1, o JSF 2 facilita a implementação de componentes customizados.


Dica 3: Pense LEGOs

Quando eu era criança, eu tinha dois brinquedos preferidos: um conjunto de química e os LEGOS. Ambos permitiam que eu criasse coisas novas combinando blocos de construção fundamentais, algo que se tornou uma fascinação por toda a vida na forma de desenvolvimento de software.

O ponto forte do JSF sempre foi seu modelo de componente, mas esse ponto forte não foi plenamente realizado até agora porque era difícil implementar componentes customizados com o JSF 1. Era necessário escrever o código Java, especificar a configuração XML, e ter uma boa compreensão do ciclo de vida de JSF. Com o JSF 2, é possível implementar componentes customizados:

  • Sem nenhuma configuração, XML ou outro.
  • Sem código Java.
  • Ao qual os desenvolvedores podem acrescentar funcionalidades.
  • Aquela ótima implementação quando modificada.

No restante deste artigo, mostrarei a você a implementação de três componentes customizados para a aplicação de locais: um ícone, um painel de login e um painel que mostra um mapa de endereço e informações do clima. Mas primeiro, darei a você uma visão geral dos componentes compostos JSF 2.

Implementação de componentes customizados

O JSF 2 combina o uso de modelos de facetas, manuseio de recurso (discutido na Parte 1), e uma simples convenção de nomenclatura para implementar componentes compostos. Os componentes compostos, como o nome sugere, permite que você componha um componente a partir de componentes existentes.

Você implementa componentes compostos em XHTML em algum ponto abaixo do diretório resources e faz um link até eles, puramente por convenção, a um espaço de nomes e uma tag. A Figura 5 mostra como organizei os componentes compostos para a aplicação de locais:

Figura 5. Os componentes da aplicação de locais

Para usar componentes compostos, é necessário declarar um espaço de nomes e usar as tags. O espaço de nomes é sempre http://java.sun.com/jsf/composite mais o nome do diretório em que o componente está localizado sob o diretório resources. O nome do componente propriamente dito é o nome de seu arquivo XHTML, sem a extensão .xhtml. Esta convenção torna óbvia a necessidade de qualquer configuração que seja. Por exemplo, para usar o componente login na aplicação de locais, você faria isto:

 <html xmlns="http://www.w3.org/1999/xhtml"    
 ...     
 xmlns:util="http://java.sun.com/jsf/composite/component/util">  
 ...   
 <util:login.../>   
 ... <html>

E para usar o componente icon, você faria isto

 <html xmlns="http://www.w3.org/1999/xhtml"     
 ...    
 xmlns:util="http://java.sun.com/jsf/composite/components/util">   
 ...   
 <util:icon.../>  
 ... <html>

Finalmente, usa-se o componente de locais assim:

 <html xmlns="http://www.w3.org/1999/xhtml"     
 ...     
 xmlns:util="http://java.sun.com/jsf/composite/components/places">  
 ...   
 <places:place.../>   
 ... <html>

O icon componente: Um componente composto simples

A aplicação de locais usa os dois ícones mostrados na Figura 6:

Figura 6. Os ícones da aplicação de locais

Cada ícone é um link. Quando um usuário clica no ícone esquerdo na Figura 6, o JSF mostra a marcação da visualização atual, enquanto que a ativação do ícone direito efetua o log-out do usuário da aplicação.

É possível especificar um nome de classe CSS e uma imagem para links, e também anexar métodos a links. O JSF invoca esses métodos quando o usuário clica no respectivo link.

A Listagem 11 mostra como o componente icon é usado na aplicação de locais para mostrar a marcação:

Listagem 11. Usando o componente icon para mostrar a marcação
 <html xmlns="http://www.w3.org/1999/xhtml"    
 xmlns:util="http://java.sun.com/jsf/composite/components/util">    
 <util:icon actionMethod="#{sourceViewer.showSource}"                        
 image="#{resource['images:disk-icon.jpg']}"/>  
 ... 
 </html>

A Listagem 12 mostra como o componente icon é usado para efetuar logout:

Listagem 12. Usando o componente icon para efetuar logout
 <html xmlns="http://www.w3.org/1999/xhtml"    
 xmlns:util="http://java.sun.com/jsf/composite/components/util">    
 <util:icon actionMethod="#{places.logout}"                       
 image="#{resource['images:back-arrow.jpg']}"/>  
 ... </html>

A Listagem 13 mostra o código para o componente icon:

Listagem 13. O componente icon
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 <html xmlns="http://www.w3.org/1999/xhtml"    
 xmlns:h="http://java.sun.com/jsf/html"     
 xmlns:composite="http://java.sun.com/jsf/composite">        
 <!-- INTERFACE -->   
 <composite:interface>     
 <composite:attribute name="image"/>     
 <composite:attribute name="actionMethod"               
 method-signature="java.lang.String action()"/>           
 </composite:interface>   
 <!-- IMPLEMENTATION -->               
 <composite:implementation>     
 <h:form>        
 <h:commandLink action="#{cc.attrs.actionMethod}" immediate="true">        
 <h:graphicImage value="#{cc.attrs.image}"                 
 styleClass="icon"/>       
 </h:commandLink>     
 </h:form>   
 </composite:implementation> 
 </html>

Como todos os componentes compostos, o componente icon na Listagem 13 contém duas seções: <composite:interface> e <composite:implementation>. A seção <composite:interface> define uma interface que pode ser usada para configurar o componente. O componente icon possui dois atributos: image, que define como se parece o componente, e actionMethod, que define como ele se comporta.

A seção <composite:implementation> contém a implementação do componente. Ela usa a expressão #{cc.attrs.ATTRIBUTE_NAME} para acessar atributos definidos na interface do componente. (O cc, que é uma palavra-chave reservada na linguagem de expressão JSF 2, representa componente composto.)

Observe que o componente icon na Listagem 13 especifica uma classe CSS para sua imagem com o atributo de <h:graphicImage>styleClass. O nome dessa classe CSS é codificado permanentemente como icon, para que seja possível apenas especificar uma classe CSS com esse nome e o JSF usará essa classe para todos os ícones na aplicação. Entretanto, e se você quiser sobrepor esse nome de classe de CSS? Nesse caso, eu poderia adicionar outro atributo para o nome de classe CSS e fornecer um padrão que será usado pelo JSF quando o atributo não for especificado. A Listagem 14 mostra a aparência desse atributo:

Listagem 14. O componente icon refatorado
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 <html xmlns="http://www.w3.org/1999/xhtml"     
 ...     
 xmlns:composite="http://java.sun.com/jsf/composite">          
 <composite:interface>       
 ...       
 <composite:attribute name="styleClass" default="icon" required="false"/>       
 ...    
 </composite:interface>     
 <composite:implementation>      
 ...       
 <h:graphicImage value="#{cc.attrs.image}"                 
 styleClass="#{cc.attrs.styleClass}"/>      
 ...     
 </composite:implementation> </html>

Na Listagem 14, adicionei um atributo à interface do componente ícone chamado styleClass e referenciei esse atributo na implementação do componente. Com essa mudança, é possível agora especificar uma classe CSS opcional para a imagem do ícone, assim:

 <util:icon actionMethod="#{places.logout}"                   
 image="#{resource['images:back-arrow.jpg']}"            
 styleClass="customIconClass"/>

Se não especificar o atributo styleClass, o JSF usará o valor padrão, icon.

O componente login: Um componente plenamente configurável

Com o JSF 2, é possível implementar componentes compostos plenamente configuráveis. Por exemplo, a aplicação de locais contém um componente login, mostrado na Figura 7:

Figura 7. O componente login da aplicação de locais

A Listagem 15 mostra como a aplicação de locais usa o componente login:

Listagem 15. Usando o componente login
 <!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:comp="http://java.sun.com/jsf/composite/component/util">   
 <util:login loginPrompt="#{msgs.loginPrompt}"                
 namePrompt="#{msgs.namePrompt}"            
 passwordPrompt="#{msgs.passwordPrompt}"               
 loginAction="#{user.login}"            
 loginButtonText="#{msgs.loginButtonText}"               
 managedBean="#{user}">                      
 <f:actionListener for="loginButton"                         
 type="com.clarity.LoginActionListener"/>                             
 </util:login>  
 ... 
 </html>

A Listagem 15 não só parametriza os atributos do componente login, tais como os avisos de nome e senha, mas também anexa um action listener ao botão Log In do componente. Esse botão é exposto pela interface do componente login, como mostra a Listagem 16:

Listagem 16. O componente login
 <!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:f="http://java.sun.com/jsf/core"     
 xmlns:h="http://java.sun.com/jsf/html"     
 xmlns:composite="http://java.sun.com/jsf/composite">    
 <!-- INTERFACE -->   
 <composite:interface>     
 <composite:actionSource name="loginButton" 
  targets="form:loginButton"/>     
 <composite:attribute name="loginButtonText" 
 default="Log In" required="true"/>     
 <composite:attribute name="loginPrompt"/>     
 <composite:attribute name="namePrompt"/>     
 <composite:attribute name="passwordPrompt"/>     
 <composite:attribute name="loginAction"        
 method-signature="java.lang.String action()"/>     
 <composite:attribute name="managedBean"/>   
 </composite:interface>        
 <!-- IMPLEMENTATION -->   
 <composite:implementation>    
 <h:form id="form" prependId="false">      
 <div class="prompt">        
 #{cc.attrs.loginPrompt}      
 </div>       
 <panelGrid columns="2">       
 #{cc.attrs.namePrompt}        
 <h:inputText id="name" value="#{cc.attrs.managedBean.name}"/>         
 #{cc.attrs.passwordPrompt}         
 <h:inputSecret id="password" value="#{cc.attrs.managedBean.password}" />       
 </panelGrid>      
 <p>       
 <h:commandButton id="loginButton"                      
 value="#{cc.attrs.loginButtonText}"                      
 action="#{cc.attrs.loginAction}"/>      
 </p>   
 </h:form>       
 <div class="error" style="padding-top:10px;">      
 <h:messages layout="table"/>    
 </div>   
 </composite:implementation>
 </html>

Na interface do componente de login, tenho exposto o botão Log In sob o nome loginButton. Esse nome aponta para o botão Log In que reside no formulário denominado form, assim o valor do atributo targets: form:loginButton.

O action listener associado ao botão Log In na Listagem 16 é exibido na Listagem 17:

Listagem 17. O action listener do botão Log In
 package com.clarity;  
 import javax.faces.event.AbortProcessingException; 
 import javax.faces.event.ActionEvent; 
 import javax.faces.event.ActionListener;  
 public class LoginActionListener 
 implements ActionListener 
 {   public void processAction(ActionEvent e)      
 throws AbortProcessingException {     
 System.out.println("logging in ...........");   
 } 
 }

O action listener na Listagem 17 destina-se apenas a fins ilustrativos — quando o usuário efetua o log in, simplesmente escrevo uma mensagem ao arquivo de log do contêiner de servlet. Mas você pegou a idéia: Com o JSF 2, é possível implementar componentes plenamente configuráveis e adicionar funcionalidades a esses componentes, tudo sem uma única linha de código Java ou configuração XML. Essa é uma grande funcionalidade.

O componente place: Aninhamento de componentes compostos

O JSF 2 permite a implementação de componentes totalmente configuráveis sem nenhum código ou configuração Java. Também é possível aninhar componentes compostos, o que permite que você divida componentes complexos em unidades menores e mais gerenciáveis. Por exemplo, a Figura 8 mostra o componente place, que exibe um mapa e informações climáticas de um determinado endereço:

Figura 8. O componente place da aplicação de locais

A Listagem 18 mostra como a aplicação de locais usa o componente place:

Listagem 18. Usando o componente place
 <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:places=
 "http://java.sun.com/jsf/composite/components/places">    
 <h:form id="form">      
 <ui:repeat value="#{places.placesList}" var="place">      
 <places:place location="#{place}"/>     
 </ui:repeat>   
 </h:form> 
 </ui:composition>

O código do componente place é exibido na Listagem 19:

Listagem 19. O componente place
 <!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:composite="http://java.sun.com/jsf/composite"     
 xmlns:places="http://java.sun.com/jsf/composite/components/places">
   
 <!--  INTERFACE -->   
 <composite:interface>     
 <composite:attribute name="location" required="true"/>       
 </composite:interface>            
 <!-- IMPLEMENTATION -->   
 <composite:implementation>     
 <div class="placeHeading">       
 <places:map     
  title="Map"/>      
  <places:weather title="Weather"/>      
 </div>   
 </composite:implementation>     
 </html>

Na Listagem 19, o componente place usa dois componentes aninhados: <places:map> e <places:weather>. A Listagem 20 exibe o componente map:

Listagem 20. O componente map
 <!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:f="http://java.sun.com/jsf/core"     
 xmlns:h="http://java.sun.com/jsf/html"    
 xmlns:ui="http://java.sun.com/jsf/facelets"    
 xmlns:composite="http://java.sun.com/jsf/composite">         
 <!-- INTERFACE -->    <composite:interface>     
 <composite:attribute name="title"/>   
 </composite:interface>             
 <!-- IMPLEMENTATION -->     
 <composite:implementation>      
 <div class="map">       
 <div style="padding-bottom: 10px;">         
 <h:outputText value="#{cc.attrs.title}"                     
 style="color: blue"/>       
 </div>                
 <h:panelGrid columns="1">        
 <h:panelGroup>          
 <div style="padding-left: 5px;">           
 <i>             
 <h:outputText value="#{cc.parent.attrs.location.streetAddress}, 
 "/>           
 </i>                         
 <h:outputText value=" #{cc.parent.attrs.location.city}" 
 />           
 <h:outputText value="#{cc.parent.attrs.location.state}"
 /><hr/>          
 </div>        
 </h:panelGroup>          
 <h:panelGrid columns="2">          
 <div style="padding-right: 10px;margin-bottom: 10px;font-size:14px">           
 #{msgs.zoomPrompt}          
 </div>            
 <h:selectOneMenu onchange="submit()"                        
 value="#{cc.parent.attrs.location.zoomIndex}"          
 valueChangeListener="#{cc.parent.attrs.location.zoomChanged}"                        
 style="font-size:13px;font-family:Palatino">             
 <f:selectItems value="#{cc.parent.attrs.location.zoomLevelItems}"/>            
 </h:selectOneMenu>       
 </h:panelGrid>          
 <h:graphicImage url="#{cc.parent.attrs.location.mapUrl}"         
 style="border: thin solid gray"/>       
 </h:panelGrid>      
 </div>    
 </composite:implementation> 
 </html>

Refatoração do componente composto

Listagem 20 — a marcação do componente map— é um pouco longa para o meu gosto. É um pouco difícil entender à primeira vista, e sua complexidade poderia apresentar problemas mais tarde.

É possível refatorar facilmente a Listagem 20 em arquivos múltiplos e mais gerenciáveis, como fiz anteriormente quando refatorei o menu esquerdo da visualização de locais nas Listagens 8, 9, e 10. Neste caso, deixarei a refatoração como exercício para você.

Observe o uso da expressão #{cc.parent.attrs.location.ATTRIBUTE_NAME} na Listagem 20. É possível usar o atributo parent do componente composto para acessar atributos do componente-pai, que facilita muito o aninhamento de componentes.

Mas você não precisa confiar estritamente nos atributos pai em componentes aninhados. Como fiz no componente de local na Listagem 19, é possível passar atributos, como o título do mapa, de um pai para o seu componente aninhado, exatamente como seria possível passar atributos a qualquer outro componente, aninhado ou não.

É um pouco anticlimático, mas a Listagem 21 mostra o componente weather:

Listagem 21. O componente weather
 <!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:composite="http://java.sun.com/jsf/composite">    
 <!-- INTERFACE -->   
 <composite:interface>     
 <composite:attribute name="title"/>   
 </composite:interface>             
 <!-- IMPLEMENTATION -->   
 <composite:implementation>         
 <div class="weather">      
 <div style="padding-bottom: 10px;">       
 <h:outputText value="#{cc.attrs.title}"          
 style="color: blue"/>     
 </div>              
 <div style="margin-top: 10px;width:250px;">       
 <h:outputText style="font-size: 12px;"                     
 value="#{cc.parent.attrs.location.weather}"                    
 escape="false"/>      
 </div>   
 </div>              
 </composite:implementation> 
 </html>

O componente weather, da mesma forma que o componente map, usa tanto um atributo de componente pai (o HTML de clima de um serviço Web de clima), quanto um atributo específico de componente (o título). (Vide Parte 1 para ver como a aplicação obtém as informações de mapa e clima de um local específico.)

Assim, ao implementar componentes aninhados, você tem uma escolha. Pode deixar o componente aninhado confiar nos atributos de seu componente pai, ou pode requerer que o componente pai passe os atributos explicitamente ao componente aninhado. Por exemplo, o componente place na Listagem 19 passa explicitamente os atributos de título aos seus componentes aninhados, porém os componentes aninhados confiam nos atributos pais, tais como a URL de mapa e HTML de clima.

Decidir implementar atributos explícitos de componente ou confiar em atributos pais é uma troca entre acoplamento e conveniência. Neste caso, os componentes map e weather são acoplados firmemente a seus componentes pais (o componente place) pois eles confiam nos atributos do componente pai. Eu poderia ter desacoplado os componentes map e weather do componente place, especificando a totalidade dos atributos dos componentes map e weather como atributos explícitos de componente. Mas nesse caso, perco alguma conveniência, pois o componente place precisaria passar todos os atributos explicitamente aos componentes map e weather.


Na próxima vez....

Neste artigo, mostrei como usar o JSF 2 para implementar UIs que são fáceis de manter e estender através do uso de modelos e componentes compostos. O artigo final nesta série mostrará como usar o Java Script em componentes compostos, como usar o novo modelo de evento do JSF 2, e como tirar vantagem do suporte incorporado do JSF 2 para Ajax.


Download

DescriçãoNomeTamanhoMétodo de download
Source code for the examplesjsf2fu2.zip7.4MBHTTP

Informações sobre métodos de download

Recursos

Aprender

Obter produtos e tecnologias

  • JSF: Baixe o JSF 2.0.

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java
ArticleID=404895
ArticleTitle=JSF 2 fu, Parte 2: Uso de modelos e componentes compostos
publish-date=05142014