É fundamental que qualquer aplicação trabalhe com o conceito de Reusabilidade de código, assim é possível ter uma maior flexibilidade no código e produtividade no tempo de entrega de um projeto. É fato que usar reusabilidade em Java é muito comum, mas como fazemos isso em XHTML com JSF? Quantas vezes você deve ter se questionado: “Eu tenho que criar a mesma tela de listagem de dados 30 vezes se eu tiver 30 painéis de listagem?” A resposta é não, e neste artigo veremos como aplicar o conceito de reusabilidade de código usando Composite Components com JSF 2.0

Introdução ao Composite Components com JSF 2.0

Desde que surgiu o JSF 2.0, é possível fazer uso dessa poderosa ferramenta conhecida por “Composite Components” que tem como principal objetivo proporcionar reusabilidade de componentes.

Para exemplificar essa questão, imagine o seguinte cenário onde o uso de tal recurso seria necessário: Você possui várias telas que realizam operações de CRUD e em todas elas você possui os botões “Novo, Salvar, Remover e Cancelar”. O normal seria você ficar criando vários commandButtons cada vez que houvesse necessidade, nesse caso em todas as telas, porém você pode criar componente customizado (Composite Component) que fará a função do seu commandButton de uma forma genérica. Ficou complicado de entender? Não se preocupe, vamos exemplificar mais à frente.

Vamos começar utilizando o Composite. Para isso, você deve declarar o Namespace logo no início da tag HTML, como na Listagem 1.

Listagem 1. Declarando o Namespace do Composite Component


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

Essa é a forma de declarar o composite no seu XHTML, igual ao “import” do Java. No composite há duas tags de suma importância que são essenciais para seu funcionamento:

1 – Interface (composite:interface): Essa tag serve para declarar todos os atributos que poderão ser passados para uso no nosso composite component, ou seja, se você quer criar um atributo chamado “nomeDoComponente”, toda vez que o seu componente for criado em qualquer lugar da aplicação deverá ser passado o atributo “nomeDoComponente”, algo parecido com: <meuscomposites:botaonovo nomeDoComponente='Novo Funcionário' />

Veja na Listagem 2 um exemplo do uso da tag interface.

Listagem 2. composite:interface


  <composite:interface>
     <composite:attribute name="pageCriarBean"
        shortDescription="Página para onde será redirecionado ao criar um novo Bean. Ex: 'manter_cartao_credito'" />
     <composite:attribute name="managedBeanName"
         type="br.com.meuprojeto.mb.BasicCrudMBNewImpl" required="true"
         shortDescription="Nome do managedBean do componente" />
      <composite:attribute name="disabledLogic"
        default="#{cc.attrs.managedBeanName.bean == null}"
        shortDescription="Lógica adicional para que o componente seja desabilitado. Ex: Quando a situação de determinado objeto for ATIVO ou INATIVO" />
  </composite:interface>

Vamos entender a lógica acima da nossa interface: estamos criando atributos para serem utilizados posteriormente, igualmente a um método em Java, onde este recebe os parâmetros e você pode utilizá-los onde quiser.

O recurso “attribute” no composite é igual ao nosso parâmetro em Java. No primeiro caso onde temos o “pageCriarBean”, estamos ainda colocando um “shortDescription” que servirá para ajudar a identificar para que serve aquele atributo, qual sua real necessidade.

No segundo caso onde temos o attribute “managedBeanName” colocamos que ele deve obrigatoriamente ser no tipo “BasicCrudMBNewImpl” e o no terceiro caso passamos um valor padrão para nosso atributo quando não for passado nada como atributo “disabledLogic”.

Então temos todos os nossos atributos definidos e onde devemos usá-los? Aqui entra o item 2.

2 – Implementation (composite:implementation): Os atributos definidos em “Interface” serão utilizados aqui, podemos utilizá-los em quantos componentes quisermos. Podemos, por exemplo, criar uma página de pesquisa padrão baseada em composite, apenas trabalhando com atributos. Vamos ver o exemplo da Listagem 3.

Listagem 3. composite:implementation


  <composite:implementation>
     <p:toolbar>
       <p:toolbarGroup align="left">
         <p:button icon="ui-icon-plus" value="Novo" id="buttonNovo" alt="Clique para adicionar um novo registro"
           outcome="#{cc.attrs.managedBeanName.redirectCriarBean(cc.attrs.pageCriarBean)}" />
   
        <p:commandButton disabled="#{cc.attrs.disabledLogic}"
          id="commandButtonRemover" value="Remover" icon="ui-icon-trash"
          onclick="varConfirmRemover.show()" type="button" />
   
       <p:confirmDialog id="confirmRemover" message="Deseja Remover ?"
          showEffect="fade" hideEffect="fade" header="Remover"
           severity="alert" widgetVar="varConfirmRemover">
          <p:commandButton value="Sim" oncomplete="varConfirmRemover.hide()"
             actionListener="#{cc.attrs.managedBeanName.deletar}" update="@form" />
          <p:commandButton value="Não" onclick="varConfirmRemover.hide()"
             type="button" />
       </p:confirmDialog>
                                                 
       <p:separator />
                                                 
      <composite:insertChildren />
     </p:toolbarGroup>
    </p:toolbar>
   </composite:implementation>

Para acessar nossos atributos via “implementation” basta fazermos “#{cc.attrs.nomedoatributo}” e utilizá-lo em qualquer parte de desejarmos. No nosso exemplo acima temos um botão novo e um botão de remover que podem ser utilizados em qualquer parte da nossa aplicação e se amanhã decidirmos mudar o ícone do botão, basta mudar em um único local, no composite.

Então veja como ficou nosso composite final, já criado com todos os atributos necessários (Listagem 4).

Listagem 4. Composite com botão de novo e remover


  <!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:p="http://primefaces.org/ui"
       xmlns:composite="http://java.sun.com/jsf/composite">
       <composite:interface>
         <composite:attribute name="pageCriarBean"
           shortDescription="Página para onde será redirecionado ao criar um novo Bean. Ex: 'manter_cartao_credito'" />
         <composite:attribute name="managedBeanName"
             type="br.com.meuprojeto.mb.BasicCrudMBNewImpl" required="true"
             shortDescription="Nome do managedBean do componente" />
         <composite:attribute name="disabledLogic"
            default="#{cc.attrs.managedBeanName.bean == null}"
            shortDescription="Lógica adicional para que o componente seja desabilitado. Ex: Quando a situação de determinado objeto for ATIVO ou INATIVO" />
    </composite:interface>
   
    <composite:implementation>
      <p:toolbar>
         <p:toolbarGroup align="left">
            <p:button icon="ui-icon-plus" value="Novo" id="buttonNovo" alt="Clique para adicionar um novo registro"
               outcome="#{cc.attrs.managedBeanName.redirectCriarBean(cc.attrs.pageCriarBean)}" />
            <p:commandButton disabled="#{cc.attrs.disabledLogic}"
              id="commandButtonRemover" value="Remover" icon="ui-icon-trash"
              onclick="varConfirmRemover.show()" type="button" />
           <p:confirmDialog id="confirmRemover" message="Deseja Remover ?"
              showEffect="fade" hideEffect="fade" header="Remover"
              severity="alert" widgetVar="varConfirmRemover">
               <p:commandButton value="Sim" oncomplete="varConfirmRemover.hide()"
                   actionListener="#{cc.attrs.managedBeanName.deletar}" update="@form" />
               <p:commandButton value="Não" onclick="varConfirmRemover.hide()"
                  type="button" />
           </p:confirmDialog>
                                                
           <p:separator />
                                    
           <composite:insertChildren />
         </p:toolbarGroup>
   
       </p:toolbar>
     </composite:implementation>
   </html>

Agora vamos ver como utilizar nosso Composite no projeto, como se fosse um componente a mais em nosso “leque de componentes”, vamos aos passos abaixo:

  1. Vamos chamar o exemplo da listagem acima de “novoAndRemover.xhtml” e devemos colocá-lo dentro do diretório “webapp/resources/minhaaplicacao/”. O diretório “minhaaplicacao” pode ter o nome que você desejar, ele será usado para chamar todos os seus composites que estão dentro dele, algo como: <minhaaplicacao:componente1>.
  2. Agora na página XHTML que você deseja usar seu componente você deve acrescentar o seguinte NameSpace: “xmlns:minhaaplicacao="http://java.sun.com/jsf/composite/minhaaplicacao"
  3. Lembre que o nome “minhaaplicacao” é exatamente o diretório que citamos no passo 1, então agora basta você utilizar os componentes que estão dentro deste diretório, que tem o mesmo nome do arquivo XHTML. Em nosso caso criamos o arquivo “novoAndRemover.xhtml”, então podemos referência-lo da seguinte forma:
<minhaaplicacao:novoAndRemover managedBeanName="#{categoriaFluxoMB}" pageCriarBean="manter_categoria" />

Perceba que chamado a tag “minhaaplicacao” e depois atribuímos os parâmetros ao nosso componente chamado 'novoAndRemover'.

InsertChildren

Você deve ter percebido na Listagem 4 que colocamos uma linha “<composite:insertChildren />” e provavelmente você deve ter se perguntado, qual a função desta linha?

Com o InsertChildren damos ainda mais poder ao nosso componente, possibilitando que ao utilizá-lo possa ser adicionado outros componentes dentro dele. Vamos explicar melhor com um exemplo usando o componente DataTable do Primefaces.

Temos um DataTable que é comum a várias páginas da nossa aplicação, então vamos criar um componente que cria um dataTable e deixa um insertChildren para que possam ser adicionadas colunas a ele. Não vamos entrar em detalhes do código do composite, pois já explicamos nas seções anteriores. Observe a Listagem 5.

Listagem 5. Criando um composite para dataTable com insertChildren


  <!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:p="http://primefaces.org/ui"
        xmlns:composite="http://java.sun.com/jsf/composite">
   
    <composite:interface>
         <composite:attribute name="idDataTable" default="dataTableListagem"
             shortDescription="ID do DataTable. Usado para referencia em outras partes do código" />
         <composite:attribute name="managedBeanName" type="br.com.meuprojeto.mb.BasicCrudMBNewImpl" required="true"
            shortDescription="Nome do managedBean do componente" />
    </composite:interface>
   
    <composite:implementation>
       <p:dataTable rowKey="#{bean.id}" var="bean"
           value="#{cc.attrs.managedBeanName.beans}" paginator="true"
           emptyMessage="Não foi encontrado nenhum registro" rows="10"
           id="#{cc.attrs.idDataTable}"
           selection="#{cc.attrs.managedBeanName.bean}" selectionMode="single">
   
            <p:ajax event="rowSelect" update="@form" />
            <p:ajax event="rowUnselect" update="@form" />
   
            <composite:insertChildren />
   
      </p:dataTable>
    </composite:implementation>
  </html>

Então temos o cabeçalho e o rodapé do nosso dataTable prontinho, basta adicionarmos as colunas, que sempre mudam de tela em tela. Vejamos como fazer isso na Listagem 6.

Listagem 6. Inserindo colunas no nosso composite


  <minhaaplicacao:appDataTable managedBeanName="#{categoriaMB}">
   
     <p:column headerText="Descrição" sortBy="#{bean.descricao}"
        filterBy="#{bean.descricao}" id="descricao"
       filterMatchMode="contains">
       <h:link outcome="#{categoriaFluxoMB.redirectAlterarBean('manter_categoria',bean.id)}" value="#{bean.descricao}" />
     </p:column>
   
      <p:column headerText="Tipo" sortBy="#{bean.tipo.descricao}"
         filterBy="#{bean.tipo.descricao}"
          filterMatchMode="contains" id="tipo">
          <h:outputText value="#{bean.tipo.descricao}" />
      </p:column>
   </minhaaplicacao:appDataTable>

Perceba que abrimos o componente com o “<minhaaplicacao>”, colocamos as colunas dentro e fechamos com “</minhaaplicacao>”, é exatamente aqui que o insertChildren faz seu trabalho, neste contexto é representa nossas colunas que estão dentro do dataTable. Perceba que o nome do nosso componente é “appDataTable” e isso significa que o nome do arquivo é “appDataTable.xhtml” que fica dentro do “webapp/resources/minhaaplicacao”.

Podemos usar 2,3,4,5 ou quantos composites quisermos em uma página XHTML, é como qualquer outro componente porém customizado para nossa necessidade. Vamos ver na Listagem 7 a junção do nosso appDataTable + novoAndRemover, dois componentes criados para necessidades distintas mas que fazem parte do mesmo contexto.

Listagem 7. Usando o composite appDataTable e novoAndRemover


  <!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:ui="http://java.sun.com/jsf/facelets"
     xmlns:p="http://primefaces.org/ui"
     xmlns:minhaaplicacao="http://java.sun.com/jsf/composite/minhaaplicacao">
     <h:head>
     </h:head>
    <h:body>
     <ui:composition template="/templates/template.xhtml">
       <ui:define name="content">
          <h:form id="formCategorias">
              <minhaaplicacao:novoAndRemover managedBeanName="#{categoriaMB}" pageCriarBean="manter_categoria" />
              <minhaaplicacao:appDataTable managedBeanName="#{categoriaMB}">
              <p:column headerText="Descrição" sortBy="#{bean.descricao}"
                  filterBy="#{bean.descricao}" id="descricao"
                   filterMatchMode="contains">
                   <h:link outcome="#{categoriaMB.redirectAlterarBean('manter_categoria',bean.id)}" value="#{bean.descricao}" />
              </p:column>
   
              <p:column headerText="Tipo" sortBy="#{bean.tipo.descricao}"
                   filterBy="#{bean.tipo.descricao}"
                   filterMatchMode="contains" id="tipo">
                    <h:outputText value="#{bean.tipo.descricao}" />
                </p:column>
              </minhaaplicacao:appDataTable>
              </h:form>
            </ui:define>
          </ui:composition>
      </h:body>
  </html>

Mostramos exemplos de como criar composites baseados em componentes que serão reutilizados em várias partes de nossa aplicação, mas podemos subir mais o nível e criar páginas inteiras com composites, por exemplo, uma página de pesquisa padrão que toda a aplicação utilizará, seja um pesquisa de funcionário, parceiro, loja, produto ou qualquer outra entidade, terá o mesmo layout e lógica baseado em nosso composite. Para finalizar abaixo vamos a Listagem 8 com o exemplo de um composite que é uma página de registro baseada em composite.

Listagem 8. Criando uma página de registro com composite


  <?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:f="http://java.sun.com/jsf/core"
     xmlns:composite="http://java.sun.com/jsf/composite"
  >
     <composite:interface>
        <composite:attribute name="nameLable" />
        <composite:attribute name="nameValue" />
        <composite:attribute name="emailLable" />
        <composite:attribute name="emailValue" />
        <composite:attribute name="registerButtonText" />
        <composite:attribute name="registerButtonAction"
           method-signature="java.lang.String action()" />
   
    </composite:interface>
   
    <composite:implementation>
   
      <h:form>
        <h:message for="textPanel" style="color:red;" />
   
          <h:panelGrid columns="2" id="textPanel">
   
             #{cc.attrs.nameLable} :
              <h:inputText id="name" value="#{cc.attrs.nameValue}" />
   
             #{cc.attrs.emailLable} :
                <h:inputText id="email" value="#{cc.attrs.emailValue}" />
   
         </h:panelGrid>
   
         <h:commandButton action="#{cc.attrs.registerButtonAction}"
            value="#{cc.attrs.registerButtonText}"
         />
      </h:form>
   
     </composite:implementation>
  </html>

Vamos ver como utilizar nossa página acima (Listagem 9).

Listagem 9. Utilizando nossa página de registro


  <?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:minhaaplicacao="http://java.sun.com/jsf/composite/minhaaplicacao"
 >
   
  <h:body>
   
   
   <minhaaplicacao:register
       nameLable="Name"
       nameValue="#{user.name}"
       emailLable="E-mail"
       emailValue="#{user.email}"
   
      registerButtonText="Register"
      registerButtonAction="#{user.registerAction}"
    />
   
  </h:body>
   
</html>

E como você já deve ter notado, o nome do nosso composite é “register.xhtml”.

É importante salientar que este artigo mostrou questões introdutórias a respeito do composite, mas que são essenciais para começar a trabalhar com o mesmo e que na verdade são as mais frequentemente utilizadas, agora parte de você a criatividade e lógica necessária para criar seus próprias composites complexos o suficiente para sustentar a reusabilidade do seu código.