Atualmente estou trabalhando em alguns projetos que possuem diversas características em comum. Para facilitar o reaproveitamento de código, criamos um módulo à parte, uma espécie de framework, com todo o código comum aos projetos, e modularizamos os projetos usando Maven.
Quando chegamos na camada de apresentação, percebemos que não estávamos reaproveitando código como nas demais camadas. Pelo contrário, os XHTMLs de várias telas eram bastante parecidos, e estávamos basicamente copiando e colando quando surgia uma tela nova. Inclusive dentro de um mesmo XHTML, muita parte do código era copiada, pois vários elementos se repetiam (ex: elementos de formulário, com um label e um campo de texto ao lado). Decidimos então criar componentes Facelets.
A criação de componentes Facelets é muito simples, basta seguir os passos abaixo:
- Criar o componente. Como exemplo, criei um chamado itemFormulario, que exibe um label, um campo de texto e as mensagens de erro correspondentes:
- Criar um arquivo de taglib para registrar os componentes criados. Este arquivo, que vou chamar de projeto.taglib.xml, será criado no diretório /WEB-INF/facelets do projeto, e será como no exemplo abaixo:
- Registrar a biblioteca de taglib no projeto, adicionando o trecho abaixo ao arquivo web.xml:
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions">
<ui:component>
<h:outputLabel for="#{id}" value="#{label}" />
<h:inputText id="#{id}" value="#{value}" />
<h:message for="#{id}" />
</ui:component>
</html>
Esse arquivo será salvo em /WEB-INF/facelets/ com o nome itemFormulario.xhtml. Neste exemplo, o componente utiliza os parâmetros id, label e value.
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://exemplo.com.br/jsf</namespace>
<tag>
<tag-name>itemFormulario</tag-name>
<source>itemFormulario.xhtml</source>
</tag>
</facelet-taglib>
Neste exemplo, registrei o componente itemFormulario. Sempre que criar um novo componente, ele deverá ser registrado neste arquivo, através de uma nova tag <tag>.
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/facelets/projeto.taglib.xml</param-value>
</context-param>
Desta forma, os componentes declarados no arquivo de taglib poderão ser usados no seu projeto, como neste exemplo:
<!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:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:t="http://myfaces.apache.org/tomahawk"
xmlns:c="http://java.sun.com/jstl/core"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:custom="http://exemplo.com.br/jsf">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>
</title>
</head>
<body>
<custom:itemFormulario id="username" label="Digite seu login:" value="#{loginController.username}" />
</body>
</html>
A partir deste ponto, precisamos compartilhar estes componentes entre os diferentes projetos. Para isso, seguimos os passos abaixo:
- Mover o conteúdo do diretório /WEB-INF/facelets (arquivo de taglib e componentes criados) para o módulo compartilhado. Colocar estes arquivos na raiz do diretório /META-INF
- Atualizar no arquivo web.xml o trecho que registra a taglib:
- Repetir o passo anterior para cada projeto que irá utilizar os componentes criados
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/META-INF/projeto.taglib.xml</param-value>
</context-param>
Se for utilizar mais de uma taglib, declare-as separadas por “;” no trecho acima.
Nas versões atuais do Facelets, a declaração da taglib no arquivo web.xml é desnecessária caso este arquivo esteja na raiz do diretório /META-INF. Os arquivos de componentes poderão ficar em outro diretório (ex: /META-INF/facelets), bastanto atualizar o arquivo de taglib de acordo com o diretório escolhido:
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "https://facelets.dev.java.net/source/browse/*checkout*/facelets/src/etc/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://exemplo.com.br/jsf</namespace>
<tag>
<tag-name>itemFormulario</tag-name>
<source>/META-INF/facelets/itemFormulario.xhtml</source>
</tag>
</facelet-taglib>
Há ainda um passo opcional, que é a criação de um arquivo TLD (taglib descriptor) para que a IDE possa validar os componentes que você criou. Um arquivo TLD tem o seguinte formato:
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
"http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
<tlib-version>1.0</tlib-version>
<jsp-version>2.0</jsp-version>
<short-name>Componentes</short-name>
<uri>http://exemplo.com.br/jsf</uri>
<display-name>Minha biblioteca de componentes</display-name>
<tag>
<name>itemFormulario</name>
<tag-class />
<body-content>empty</body-content>
<description>
Cria um item de formulário com label, campo de texto e mensagens de erro.
</description>
<attribute>
<name>id</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
<description>
Identificação do componente
</description>
</attribute>
<attribute>
<name>label</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
<description>
Texto exibido no label do componente
</description>
</attribute>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
<type>java.lang.String</type>
<description>
Valor associado ao inputText do componente
</description>
</attribute>
</tag>
</taglib>
Salve esse arquivo com o nome projeto.taglib.tld, no mesmo diretório do arquivo projeto.taglib.xml. Agora, ao abrir um arquivo XHTML que esteja usando o componente itemFormulario, a IDE exibirá os erros de validação (ex: um atributo marcado como required não foi definido). Com este arquivo criado, ao abrir um XHTML no Eclipse usando o editor de JSP, você terá também o recurso de autocomplete (Control + espaço), que exibirá todos os atributos do componente, assim como a descrição de cada um.
Referências:
- Create a Common Facelets Tag Library: Share it across projects
- Reuse facelets xhtml files and taglibs from jar archives


E se eu quiser colocar estilos CSS nas tags? Como vc faz aí?
Romulo, não entendi a pergunta. Você quer acrescentar estilos aos componentes criados, ou quer compartilhar arquivos CSS entre projetos?
Acredito que ele queira aplicar estilos aos inputs que estão dentro do componente.
Minha sugestão, é criar parâmetros para todos os atributos de estilo, como style e styleClass, mas não obrigatórios.
Para testar se o parâmetro é obrigatório, eu faço assim:
—– Componente —–
——————–
Eu iso JSTL pois a validação será feita ANTES do parse realizado pelo Facelets, resultando em um XHML “pronto” para o Facelets, com os valores padrões setados caso não estejam presentes.
Assim, podes setar estilos e classes CSS nos componentes criados.
Isso serve para todos os outros parâmetros que não sejam obrigatórios.
O Worpress “barrou” meu código..
Acho que agora vai:
—– Componente —–
<c:if test=”#{styleClass}”>
<c:set value=”false” var=”styleClass” />
</c:if>
<h:inputText value=”#{value}” styleClass=”#{styleClass}” />
——————–
Valeu pela dica Alexandre!
É exatamente o que alexandre falou. Porque pelo que eu vi de componentes com facelets, eu tenho que definir “N” variáveis.
Talvez vc tivesse feito de uma maneira diferente!
Valeu
Muito bom artigo, bem descritivo, valeu!!
Abração
Fala guilherme deixa eu te perguntar uma coisa, eu estava querendo criar um componente, soh que esse componente precisa usar uma classe, que no caso era um bean, colocando esse componente em outro projeto, como eu passaria esse bean para o outro projeto tambem ?? eu tentei apenas colocar a classe no projeto, com o @ManagedBean(name=”selectBox”)
e no componente chamando essa classe pela etl #{selectBox.lista}, soh que não está chamando a classe, eu estou usando jsf 2 , teria como fazer isso ?
abraçoo
opa, acabei de postar uma pergunta ai, mas o autocomplete do chrome no input selecionou esse felipe no nome sem querer, tem como mudar ai depois guilherme??
valeuu abraçooo
Túlio, você precisa mesmo acessar o managed bean no componente? Você poderia passar esse #{selectBox.lista} como um parâmetro para o componente:
e no componente você poderia acessar esse parametro diretamente.
Eu realmente preciso, pois eu passo como parametro um valor, ai dentro do componente, eu faço uma busca no request por um outro valor.
Isso é pra funcionar uma imagem em bytes dentro de um dataTable.
Não tem jeito ?
Nunca tentei fazer isso, mas acredito que deve funcionar se o managed bean tiver o mesmo nome em todos os projetos.
Eu queria criar um componente com um selecOneMenu, mas como eu passo os f:selectItem ??? o f:selectItems eu consigo por.
Paulo,
seu código não apareceu no comentário… Mas para criar um selectOneMenu, você pode passar como parâmetro uma lista, e gerar os itens desta forma:
<h:selectOneMenu value=”#{target}”>
<c:forEach items=”#{source}” var=”item”>
<f:selectItem itemLabel=”#{item.nome}” itemValue=”#{item.id}” />
</c:forEach>
</h:selectManyCheckbox>
Neste exemplo, o componente receberia os parâmetros source e target, onde source é a lista de entidades que você quer adicionar no menu, e target é o valor que será retornado. O selectItem recebe como parâmetros itemLabel (texto que aparece na tela) e itemValue (valor que será retornado se este item for selecionado).
Achei muito interessante o post. Aonde trabalho usamos o framework IceFaces e estamos migrando para a versão 2.0. Com isso adotaremos o facelets. Seria interessante um post sobre como criar templates facelets
Fica a dica.
Abrassss
Muito bom esse tuto, mas tenho uma pergunta…
Caso eu crie um projeto so com os componentes e disso eu crie um jar, como eu faria para usa-lo em outros projetos, so importando o jar, ou precisaria declarar no xml tambem… gostaria, se possivel de algum exemplo.
VLW
Bruno,
nunca tentei criar um jar só com os componentes, mas um dos links que citei no post (http://ocpsoft.com/opensource/create-common-facelets-jar/) descreve um exemplo.