17 de março de 2011

JSF 2.0: Utilização de Facelets

O que é o Facelets?
Segundo a especificação do Java EE 6:
"Facelets is a powerful but lightweight page declaration language that is used to build JavaServer Faces views using HTML style templates and to build component trees." Java EE 6
 Traduzindo... Facelets é uma poderosa e leve linguagem de declaração de página que é usada para construir visualizações  em JSF usando estilos de templates HTML e para construir árvores de componentes, que utiliza da tecnologia XHTML para a criação de páginas web.
O mais interessante e utilizado desta tecnologia é a possibilidade de criar templates para páginas JSF.
Agora vamos fazer alguns exemplos de utilização das 11 tags do Facelets, para isso crie um projeto JEE 6 (Dessa forma), feito isso, vamos começar:


<ui:component > 
Esta tag serve para criar componentes customizados em JSF.
Dentro do WEB-INF crie um diretório chamado componentes. Dentro deste diretório crie um arquivo xhtml com o nome meuComponente.xhtml que será nosso componente, segue o código:
<?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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <ui:component>
        <h:form>
            <h:inputText id="#{id}" value="#{value}" required="true" requiredMessage="Este campo obrigatório" />
            <h:message for="#{id}" />
            <h:commandButton value="Enviar" />
        </h:form>
    </ui:component>
</html>
Veja que no código acima usamos a tag ui:component, que define nosso componente, mas apenas isso não é suficiente para fazer ele funcionar, precisamos de um xml para mapear esse componente, então para isso crie um xml dentro do diretório componentes com o nome de facelets.taglib.xml, nele teremos o seguinte código, que explicarei abaixo:
<?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://javasemcafe.blogspot.com</namespace>
    <tag>
        <tag-name>componenteJSF</tag-name>
        <source>meuComponente.xhtml</source>
    </tag>
</facelet-taglib>
Bom... a tag namespace pode ser preenchida por um link qualquer. A tag tag-name será o nome pelo qual nosso componente será chamado, e a tag source corresponde a localização do nosso componente em xhtml. Além de tudo isso precisamos registrar o xml criado dentro do web.xml que está dentro do WEB-INF, logo após a tag web-app insira o seguinte código:
<context-param>
    <param-name>facelets.LIBRARIES</param-name>
    <param-value>/WEB-INF/componentes/facelets.taglib.xml</param-value>
</context-param>
Agora sim podemos usar nosso componente, para isso, vamos usar a página index.xhtml criada por padrão ao criar o projeto, ela deverá ficar assim:
<?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:h="http://java.sun.com/jsf/html"
      xmlns:andii="http://javasemcafe.blogspot.com">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <andii:componenteJSF id="meu" />
    </h:body>
</html>
Veja que na tag html foi adicionado o namespace definido no facelets.taglib.xml para assim podermos usar a tag andii:componenteJSF .
Visualmente ele ficará dessa forma:


Bom.. quanto a essa tag do Facelets, acabei nem me aprofundando, apenas aprendi o básico para saber para o que ela servia.

<ui:debug > 
Serve para abrir uma janela de debug quando pressionado as teclas: CTRL + SHIFT + a tecla definida na tag ui:debug, para teste inclua essa tag na página index.xhtml, lembre-se de adicionar na tag html, o namespace para utilização do facelets, ficando dessa forma:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:andii="http://javasemcafe.blogspot.com"
      xmlns:ui="http://java.sun.com/jsf/facelets">
E ainda no index.xhtml inclua dentro do h:body, a seguinte tag:
<ui:debug hotkey="K" />
Neste caso estamos usando a letra K para ser nossa hotkey, então para testar, abra no navegador a página  index.jsf, a tag ui:debug não ficará visível, apenas pressione CTRL+SHIFT+K, a seguinte tela deverá aparecer:


<ui:define > , <ui:insert > e  <ui:composition >
Estas são as tags mais utilizadas quando se fala em Facelets, elas são para definir o template das páginas, como isso ocorre? a tag ui:insert "demarca" onde será, por exemplo: o menu, o topo, o corpo do sistema. A ui:define é utilizada nas páginas que desejam utilizar o template, então... um ui:define é correspondente a um ui:insert do template. E por fim, a ui:composition serve para compor páginas que utilizam a tecnologia Facelets. Para testarmos isso vamos criar um template onde terá um menu vertical que ocupará 30% da página, e os outros 70% corresponderão ao corpo da página, para isso crie uma página JSF dentro de Páginas Web, com o nome de template.xhtml, com o código:
<?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:h="http://java.sun.com/jsf/html">

    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Facelets Template</title>
        <style type="text/css">
            .styleMenu {
                background-color: yellow;
                width: 30%;
                margin: 0; padding: 0;
                float: left;
            }

            .styleCorpo {
                background-color: red;
                width: 70%;
                float: right;
                text-align: center;
            }
        </style>
    </h:head>
    <h:body>

        <div class="styleMenu">
            <ui:insert name="menu">menu original</ui:insert>
        </div>
        
        <div class="styleCorpo">
            <ui:insert name="corpo">Corpo original do template</ui:insert>
        </div>

    </h:body>
</html>
No código acima veja que na realidade o menu e o corpo são demarcados por estilos de css nas div.
E para testar esse template, crie uma página JSF com o nome de clienteTemplate.xhtml, que corresponderia a qualquer página que utilizaria um template, assim:
<?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">

    <ui:composition template="template.xhtml">

        <ui:define name="corpo">
            isso é do clienteTemplate
        </ui:define>
        
    </ui:composition>
</html>
O resultado será este:


Agora analise o código do clienteTemplate.xhtml e veja que não foi criado um ui:define para o menu, isso aconteceu porque eu não queria que o menu fosse sobrescrito, ou seja, eu quis utilizar o que tinha dentro do template que é o padrão, a menos que você sobrescreva ele terá outro resultado.
DICA: Para quem usa o NetBeans 6.9.1, ele já dá a opção de criar templates pré definidos, como fazer isso? Clique com o botão direito em Páginas Web - Outro... selecione a categoria JavaServer Faces e use Modelo de Facelets para criar o template, aparecerá a seguinte tela:


Nela informe o nome para o template, e selecione o Estilo de layout, para selecionar basta clicar no layout escolhido, e clique em Finalizar...
Já para usar um template, faça os mesmos primeiros passos para criar o template, mas no lugar de escolher Modelo de Facelets, escolha Cliente de modelo de facelets, e aparecerá a seguinte janela:


Nela informe um nome para a página, e lembre-se de selecionar o modelo de template que foi criado, eu geralmente prefiro usar a Marcação de raiz gerada como ui:composition, pelo fato de estar usando Facelets mesmo.
Bom... agora tenha criatividade para criar/editar o template da forma que achar melhor :)

<ui:decorate > 
Esta tem a mesma funcionalidade que a tag ui:composition: serem utilizadas para fazer a composição de páginas que utilizam Facelets, com a diferença de que quando é utilizado a tag ui:composition, o que estiver fora desta, não aparecerá na aplicação, ou seja será ignorada. Já ao usar a ui:decorate, o que for feito fora desta tag será visualizada normalmente, para testarmos isso, na página clienteTemplate.xhtml que foi criada logo acima, depois da tag </ui:composition> e antes da </html>, escreva qualquer coisa, e mande atualizar a visualização, e veja que nada de diferente acontece... pois o que foi escrito fora da ui:composition foi ignorado.
Agora para testar o ui:decorate, nessa mesma página, troque o ui:composition pelo ui:decorate, salve e visualize o resultado, neste caso o que tinha sido escrito fora da tag, aparece normalmente no final da página. A imagem a seguir utiliza a tag ui:decorate:


<ui:include >  e <ui:fragment > 
A tag ui:include serve para incluir páginas dentro de outras páginas assim como o próprio include do PHP, por um exemplo, quando você ver que um código fonte de uma página vai ficar muito extenso, você pode separar ele em uma outra página, para a visualização isso não vai fazer diferença, pois na hora de renderizar ele irá renderizar como uma unica página, a diferença é que para dar manutenção em páginas não muito extensa é mais fácil. Já a tag ui:fragment, não consegui entender muito bem a diferença em usar ela ou o ui:composition, mas pelo o que andei lendo, o pessoal costuma usar ela nas páginas que forem incluídas pelo ui:include, vamos fazer um exemplo usando essa combinação, primeiro crie uma página JSF que será a página a ser incluída, use o nome de fragTeste.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">
<ui:fragment xmlns="http://www.w3.org/1999/xhtml"
             xmlns:h="http://java.sun.com/jsf/html"
             xmlns:ui="http://java.sun.com/jsf/facelets">

    <h:outputText value="Essa texto é da página fragTeste" />

</ui:fragment>
Agora crie uma página JSF com o nome de include.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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Facelet Title</title>
        <f:view contentType="text/html" />
    </h:head>
    <h:body>
        <h:outputText value="Esse texto é da página include!" />
        <br/>
        <ui:include src="fragTeste.xhtml" />
    </h:body>
</html>
O resultado da visualização da página include.xhtml é o seguinte:


Obs: Caso alguém saiba realmente o que a tag ui:fragment faz de diferente, ficaria grata se me informasse.

<ui:remove > e <ui:repeat >
A tag ui:remove serve para remover um código que é inserido dentro dela, ou seja, o que for escrito entre ela não será visualizado (bom... não consigo enxergar a utilização dela na prática), crie uma página JSF com o nome de remove.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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:outputText value="Isso aparece na minha pagina remove... pois está fora da tag ui:remove" />
        
        <ui:remove>
            <h:outputText value="Isso não aparece na minha página remove!!" />
        </ui:remove>
    </h:body>
</html>
A visualização será:


Já a tag ui:repeat funciona praticamente como um laço de repetição para ler valores de uma lista de um bean, então... na raiz mesmo de Pacotes de código-fonte, crie uma classe com o nome de ClienteBean.java nessa classe teremos uma lista que será lida pela tag ui:repeat. Segue o conteúdo da classe:
@Named
@javax.enterprise.context.RequestScoped
public class ClienteBean {

    private List<String> clientes;

    public ClienteBean() {
        clientes = new ArrayList<String>();
        clientes.add("Maria");
        clientes.add("Joao");
        clientes.add("Aparecida");
    }

    public List<String> getClientes() {
        return clientes;
    }

    public void setClientes(List<String> clientes) {
        this.clientes = clientes;
    }
}

Agora crie uma nova página JSF, com o nome de repeat.xhtml, segue:
<?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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>

        <ui:repeat var="cli" value="#{clienteBean.clientes}">
            <h:outputText value="#{cli}" /><br />
        </ui:repeat>
        
    </h:body>
</html>
Veja que essa tag tem seu funcionamento bem parecido com uma h:dataTable, então não tem o que questionar dela. A visualização da mesma:


<ui:param > 
A tag ui:param serve para passar valores para outra página, esse valor pode ser estático ou dinâmico(vindo de um bean), não cheguei a usar essa tag em algum projeto, mas li que o pessoal usa ela bastante para passar o usuário logado de uma página para outra...
Vamos fazer um teste com um valor estático mesmo, primeiro crie uma página JSF, com o nome de recebeParam.xhtml  esta será a página que receberá o parâmetro vindo de uma outra página que irá chamá-la. O parâmetro a ser recebido nesse exemplo será o nome:
<?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:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        
        <h:outputText value="Esta página estará recebendo um parâmetro vindo de uma outra página:" />
        <br />
        <h:outputText value="Parâmetro recebido: #{nome}" />

    </h:body>
</html>
O parâmetro será capturado usando a EL #{nome}, então na página que chamará ela, precisa ser passado um parâmetro com o name  de nome. Então agora crie uma nova página JSF com o nome de enviaParam.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:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <ui:param name="nome" value="andii.brunetta" />

        <h:form>
            <h:commandButton value="Chama a página recebeParam" action="recebeParam" />
        </h:form>
    </h:body>
</html>
Perceba que não é necessário que a tag ui:param esteja dentro do h:form, ou seja, ele não precisa de um form para ser encaminhado para a outra página, o form só está aí por causa do h:commandButton.
Essa é a visualização da página acima:


Ao clicar no botão da página mostrada acima, esta chamará a recebeParam.xhtml, que terá o seguinte resultado:




IMPORTANTE:
Caso você use o Google Chrome (indico usar ele!), ao testar alguns exemplos ele poderá dar um erro, a solução é acrescentar a seguinte tag dentro do h:head da página ou se estiver usando template, será no template mesmo:
<f:view contentType="text/html" />

Para mais informações acesse a documentação do Facelets: Tag Library Documentation - Facelets

Bom até que enfim finalizamos essa postagem... esta ficou bemmmmm extensa, mas está completa com as todas as tags do Facelets! Caso alguém tenha mais informações sobre elas, pode complementar a postagem comentando abaixo! ;)

20 comentários:

  1. Olá Andii, parabéns pelo belíssimo post. Com certeza a internet estava precisando de tutorial claro e objetivo como esse.

    Parabens!

    ResponderExcluir
  2. Obrigada Wagner! É uma honra pra mim receber um comentário desses vindo de você! :D

    ResponderExcluir
  3. Olá Alessandro! Que bom que pude ajudar :)

    ResponderExcluir
  4. Parabéns. Ótimo tutorial.

    ResponderExcluir
  5. Oláa Andi!, vlw pelo post, mto útil!!,
    Porém minha dúvida estaria voltada a outro problema: Como alterar o conteúdo do "center" do Layout do PrimeFaces em tempo real(sem ter q chamar outra page replicando o código dos menus, etc..), e aqui achei a resp.: http://forum.primefaces.org/viewtopic.php?f=3&t=2651&start=10.
    Não sei se te interessa, mas tenho certeza que seria bacana se vc tipo, criasse outro post explicando como fazer isso para o pessoal!, pois seu modo de explicação está mto simples e funcional!
    BJss

    ResponderExcluir
  6. Olá John! Obrigada pela dica, cheguei hoje de viagem, mas logo logo que eu me organizar aqui dou uma olhada. :)

    ResponderExcluir
  7. Olá andii.brunetta, gostei muito do seu post e fiquei com duvida a respeito do , conseguir fazer com um valor estático só que não consigo com uma valor vindo de um bean, exemplo:
    na minha dataTable eu trago em uma lista minha mensagens, sendo que nessa lista há links para editar e tudo mais, há tambem um link que me redireciona pra outra pagina, só que o problema é que não consigo que meu objeto apareça na página, faço o ui:param e nada, só consigo valores estáticos, tem como você colocar um exemplo passando objetos vindo de um bean. muito obrigado!!!!

    ResponderExcluir
  8. Marcelo, que escopo vc está usando? se for requestScoped, pode ser esse o problema

    ResponderExcluir
  9. Parabéns!!
    Esse blog tem me auxiliado bastante.

    ResponderExcluir
    Respostas
    1. Obrigada! Que bom que está te ajudando :)

      Excluir
  10. ola o meu insert não funcionou e fiz do q esta ai? em q configurar alguma coisa,

    ResponderExcluir
    Respostas
    1. André, neste exemplo não estamos fazendo nada em bd, são apenas exemplos de como usar o Facelets.

      Excluir
  11. Incrivel explicação sobre as tag do Facelets! É bom encontrar pessoas que querem passar o conhecimento de uma tecnologia e não apenas um tutorial =/ Parabens

    ResponderExcluir
  12. Parabéns! Excelente post. Formidável! Você tem uma didática fantástica! Parabéns mesmo.

    ResponderExcluir
  13. Olá Andii,

    muito bom o seu post. Parabéns.

    Sobre a tag geralmente é utilizada para renderizar algo fazendo binding com o seu BB.

    http://stackoverflow.com/questions/3713468/alternative-to-uifragment-in-jsf

    Só uma dica! Abraços e sucesso!

    ResponderExcluir
  14. Muito bom mesmo... isso vai me ajudar muito... Obrigado e parabéns pelo material !

    ResponderExcluir
  15. Muito bom mesmo, me ajudou bastante e fácil de entender. Abraço

    ResponderExcluir

Deixe seu comentário... ;)