Skip to content

5. Introdução à biblioteca de componentes PrimeFaces

5.1. O papel do PrimeFaces numa aplicação JSF

Voltemos à arquitetura de uma aplicação JSF, tal como a estudámos no início deste documento:

As páginas JSF foram criadas utilizando três bibliotecas de tags:

1
2
3
4
<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">
  • linha 2: as tags <h:x> do namespace [http://java.sun.com/jsf/html], que correspondem a tags HTML,
  • linha 3: as tags <f:y> no namespace [http://java.sun.com/jsf/core] que correspondem a tags JSF,
  • linha 4: as tags <ui:z> do namespace [http://java.sun.com/jsf/facelets], que correspondem às tags facelet.

Para criar páginas JSF, vamos adicionar uma quarta biblioteca de tags, a dos componentes PrimeFaces.

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
      ...
xmlns:p="http://primefaces.org/ui">
  • Linha 3: As tags <p:z> do namespace [http://primefaces.org/ui] correspondem aos componentes PrimeFaces.

Esta é a única alteração que será feita. Por isso, aparece nas visualizações. Os manipuladores de eventos e os modelos permanecem os mesmos que eram com o JSF. Este é um ponto importante a compreender.

A utilização de componentes PrimeFaces permite criar interfaces web mais intuitivas, graças aos muitos componentes desta biblioteca, e mais fluidas, graças à tecnologia AJAX que suporta nativamente. Estas são designadas por interfaces ricas ou RIAs (Rich Internet Applications).

A arquitetura JSF anterior passará a ser a seguinte arquitetura PF (PrimeFaces):

5.2. As vantagens do PrimeFaces

O site do PrimeFaces [http://www.primefaces.org/showcase/ui/home.jsf] fornece uma lista de componentes que podem ser utilizados numa página PF:

Nos exemplos a seguir, utilizaremos as duas primeiras funcionalidades do PrimeFaces:

  • alguns dos cerca de cem componentes oferecidos,
  • o seu comportamento AJAX nativo.

Entre os componentes disponíveis:

Iremos utilizar apenas cerca de quinze deles nos nossos exemplos, mas isso será suficiente para compreender os princípios da construção de uma página PrimeFaces.

5.3. Aprendendo o PrimeFaces

O PrimeFaces fornece exemplos de utilização para cada um dos seus componentes. Basta clicar na ligação. Vejamos um exemplo:

  • em [1], o exemplo para o componente [Spinner],
  • em [2], a caixa de diálogo exibida após clicar no botão [Submit].

Existem aqui três novas funcionalidades para nós:

  • o componente [Spinner], que não existe por predefinição no JSF,
  • o mesmo se aplica à caixa de diálogo,
  • e, finalmente, o POST acionado pelo botão [Submit] é tratado via AJAX. Se observar atentamente o navegador durante o POST, não verá a ampulheta. A página não é recarregada. É simplesmente modificada: um novo componente — neste caso, a caixa de diálogo — aparece na página.

Vamos ver como tudo isto funciona. O código XHTML para o exemplo é o seguinte:


<h:form>
       <p:panel header="Spinners">
           <h:panelGrid id="grid" columns="2" cellpadding="5">
                <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
                <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
                <h:outputLabel for="spinnerStep" value="Step Factor: " />
                <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
                <h:outputLabel for="minmax" value="Min/Max: " />
                <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
                <h:outputLabel for="prefix" value="Prefix: " />
                <p:spinner id="prefix" value="0" prefix="$" min="0" value="#{spinnerController.number4}"/>
           <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
           <p:outputPanel>
                   <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
                      <p:ajax update="ajaxspinnervalue" process="@this" />
               </p:spinner>
               <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
            </p:outputPanel>
           </h:panelGrid>
       </p:panel>
    <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
    
    <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        ...
     </p:dialog>
</h:form>

Primeiro, repare que vemos tags JSF padrão: <h:form> na linha 1, <h:panelGrid> na linha 3, <h:outputLabel> na linha 4. Algumas tags JSF são reutilizadas pelo PF e melhoradas: <p:commandButton> na linha 21. Em seguida, encontramos as tags de formatação do PF: <p:panel> na linha 2, <p:outputPanel> na linha 13, <p:dialog> na linha 23. Por fim, temos as tags de entrada: <p:spinner> na linha 5.

Vamos analisar este código em relação à vista:

  • em [1], o componente criado com a tag <p:panel> na linha 2,
  • em [2], o campo de entrada criado pela combinação das tags <p:outputLabel> e <p:spinner> nas linhas 6 e 7,
  • em [3], o botão POST criado com a tag <p:commandButton> na linha 21,
  • em [4], a caixa de diálogo das linhas 23–25,
  • em [5], um contentor invisível para dois componentes. É criado pela tag <p:outputPanel> na linha 13.

Vamos analisar o código seguinte que implementa uma ação AJAX:


           <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
           <p:outputPanel>
                   <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
                      <p:ajax update="ajaxspinnervalue" process="@this" />
               </p:spinner>
               <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
</p:outputPanel>

Este código gera a seguinte visualização:

  • linha 1: exibe o texto [1]. Também serve como rótulo para o componente com id=ajaxspinner (atributo). Este componente é o da linha 3 (atributo id),
  • Linhas 3–5: exibem o componente [2]. Este componente é um componente de entrada/exibição associado ao modelo #{spinnerController.number5} (atributo value),
  • linha 6: exibe o componente [3]. Este componente é um componente de exibição ligado ao modelo #{spinnerController.number5} (atributo value),
  • linha 4: a tag <p:ajax> adiciona comportamento AJAX ao spinner. Sempre que o spinner altera o seu valor, é enviada uma solicitação POST contendo esse valor (atributo process="@this") para o modelo #{spinnerController.number5}. Assim que isto é feito, a página é atualizada (atributo update). O valor deste atributo é o ID de um componente na página, neste caso o da linha 6. O componente a que o atributo update se refere é então atualizado com o modelo. Este é novamente #{spinnerController.number5}, ou seja, o valor do spinner. Assim, o campo [3] segue as entradas do campo [2].

Trata-se de um comportamento AJAX, um acrónimo que significa Asynchronous JavaScript and XML. De um modo geral, um comportamento AJAX funciona da seguinte forma:

  • o navegador apresenta uma página HTML que contém código JavaScript (o «J» em AJAX). Os elementos da página formam um objeto JavaScript chamado DOM (Document Object Model),
  • o servidor hospeda a aplicação web que gerou esta página,
  • em [1], ocorre um evento na página. Por exemplo, o indicador de carregamento avança. Este evento é tratado pelo JavaScript,
  • em [2], o JavaScript envia um pedido POST para a aplicação web. Faz-o de forma assíncrona (o A em AJAX). O utilizador pode continuar a trabalhar com a página. Esta não fica congelada, mas pode ser congelada se necessário. O POST atualiza o modelo da página com base nos valores enviados, neste caso o modelo #{spinnerController.number5},
  • em [3], a aplicação web envia uma resposta XML (o X em AJAX) ou JSON (JavaScript Object Notation) de volta para o JavaScript,
  • em [4], o JavaScript utiliza esta resposta para atualizar uma área específica do DOM, neste caso a área com id=ajaxspinnervalue.

Ao utilizar JSF e PrimeFaces, o JavaScript é gerado pelo PrimeFaces. Esta biblioteca depende da biblioteca JavaScript jQuery. Da mesma forma, os componentes do PrimeFaces dependem dos componentes da biblioteca jQuery UI (User Interface). Portanto, o jQuery constitui a base do PrimeFaces.

Voltemos ao nosso exemplo e analisemos agora o pedido POST do botão [Submit]:

O código associado ao POST é o seguinte:


<p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
    
    <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
            <h:outputText value="Value 1: " />
            <h:outputText value="#{spinnerController.number1}" /> 
            
            <h:outputText value="Value 2: " />
            <h:outputText value="#{spinnerController.number2}" /> 
            
            <h:outputText value="Value 3: " />
            <h:outputText value="#{spinnerController.number3}" /> 
            
            <h:outputText value="Value 4: " />
            <h:outputText value="#{spinnerController.number4}" /> 
            
            <h:outputText value="Value 5: " />
            <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
     </p:dialog>
                
</h:form>
  • Linha 1: O pedido POST é acionado pelo botão na linha 1. No PrimeFaces, as tags que acionam um pedido POST fazem-no, por predefinição, através de uma chamada AJAX. É por isso que estas tags possuem um atributo `update` para especificar a área a ser atualizada assim que a resposta do servidor for recebida. Aqui, a área a ser atualizada é o `panelGrid` na linha 4. Assim, quando a resposta POST for recebida, esta área será atualizada com os valores enviados para o modelo. No entanto, estes valores estão contidos numa caixa de diálogo que está oculta por predefinição. É o atributo oncomplete na linha 1 que a exibe. Este evento ocorre no final do processamento POST. O valor deste atributo é código JavaScript. Aqui, exibimos a caixa de diálogo com id=dialog, que é a da linha 3 (atributo widgetVar),
  • Linha 3: Vemos vários atributos da caixa de diálogo. Terá de experimentar para ver o que fazem.

Já mencionámos o modelo, mas ainda não o mostrámos. Aqui está ele:

public class SpinnerController {

    private int number1;
    private double number2;
    private int number3;
    private int number4;
    private int number5;

    // getters and setters
...
}

De um modo geral, pode proceder da seguinte forma:

  • identifique o componente PrimeFaces que pretende utilizar,
  • estude o seu exemplo. Os exemplos do PrimeFaces são bem escritos e fáceis de compreender.

5.4. Um primeiro projeto PrimeFaces: mv-pf-01

Vamos criar um projeto web Maven com o NetBeans:

  • [1, 2, 3]: criar um projeto Maven do tipo [Aplicação Web],
  • [4]: o servidor será o Tomcat,
  • em [5], o projeto gerado,
  • em [6], remova o ficheiro [index.jsp] e o pacote Java,
  • em [7, 8]: nas propriedades do projeto, adicionamos suporte para Java Server Faces,
  • em [9], no separador [Componentes], selecione a biblioteca de componentes PrimeFaces. O NetBeans oferece suporte para outras bibliotecas de componentes: ICEFaces e RichFaces.
  • Em [10], o projeto gerado. Em [11], repare na dependência do PrimeFaces.

Em resumo, um projeto PrimeFaces é um projeto JSF padrão ao qual foi adicionada uma dependência do PrimeFaces. Nada mais.

Agora que compreendemos isto, modificamos o ficheiro [pom.xml] para funcionar com as versões mais recentes das bibliotecas:


    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.1.8</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>3.3</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>javax</groupId>
      <artifactId>javaee-web-api</artifactId>
      <version>6.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
  <repositories>
    <repository>
      <id>jsf20</id>
      <name>Repository for library Library[jsf20]</name>
      <url>http://download.java.net/maven/2/</url>
    </repository>
    <repository>
      <id>primefaces</id>
      <name>Repository for library Library[primefaces]</name>
      <url>http://repository.primefaces.org/</url>
    </repository>
</repositories>

Linhas 26–30: Repare no repositório Maven para o PrimeFaces. Depois de efetuar estas alterações, compile o projeto para iniciar o download das dependências. Obterá então o projeto [12].

Agora, vamos tentar reproduzir o exemplo que estudámos. A página [index.html] fica da seguinte forma:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:head>
    <title>Spinner</title>
  </h:head>
  <h:body>
    <!-- form -->
    <h:form>
      <p:panel header="Spinners">
        <h:panelGrid id="grid" columns="2" cellpadding="5">
          <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
          <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
          <h:outputLabel for="spinnerStep" value="Step Factor: " />
          <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
          <h:outputLabel for="minmax" value="Min/Max: " />
          <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
          <h:outputLabel for="prefix" value="Prefix: " />
          <p:spinner id="prefix" prefix="$" min="0" value="#{spinnerController.number4}"/>
          <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
          <p:outputPanel>
            <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
              <p:ajax update="ajaxspinnervalue" process="@this" />
            </p:spinner>
            <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
          </p:outputPanel>
        </h:panelGrid>
      </p:panel>
      <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
      <!-- dialog box -->
      <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
          <h:outputText value="Value 1: " />
          <h:outputText value="#{spinnerController.number1}" /> 
          <h:outputText value="Value 2: " />
          <h:outputText value="#{spinnerController.number2}" /> 
          <h:outputText value="Value 3: " />
          <h:outputText value="#{spinnerController.number3}" /> 
          <h:outputText value="Value 4: " />
          <h:outputText value="#{spinnerController.number4}" /> 
          <h:outputText value="Value 5: " />
          <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
      </p:dialog>
    </h:form>
  </h:body>
</html>

Não se esqueça da linha 5, que declara o namespace para a biblioteca de tags PrimeFaces. Adicione ao projeto o bean que serve de modelo para a página:

  

O código é o seguinte:


package beans;
 
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
 
@ManagedBean
@RequestScoped
public class SpinnerController {
 
  // model
  private int number1;
  private double number2;
  private int number3;
  private int number4;
  private int number5;
 
  // getters and setters
  ...
}

A classe é um bean (linha 6) com âmbito de solicitação (linha 7). Como não foi especificado nenhum nome, o bean recebe o nome da classe com a primeira letra minúscula: spinnerController.

Quando executamos o projeto, obtemos o seguinte:

 

Acabámos de demonstrar como testar um exemplo retirado do site da PrimeFaces. Todos os exemplos podem ser testados desta forma.

Daqui em diante, iremos concentrar-nos apenas em determinados componentes do PrimeFaces. Primeiro, revisitaremos os exemplos estudados com o JSF e substituiremos certas tags JSF por tags do PrimeFaces. A aparência das páginas mudará ligeiramente; elas terão comportamento AJAX, mas os beans associados não precisarão de ser alterados. Em cada um dos próximos exemplos, iremos simplesmente apresentar o código XHTML das páginas e as capturas de ecrã associadas. Os leitores são encorajados a testar os exemplos para identificar as diferenças entre as páginas JSF e as páginas PF.

5.5. Exemplo mv-pf-02: Gestor de eventos – Internacionalização – Navegação na página

Este projeto é uma adaptação do projeto JSF [mv-jsf2-02] (secção 2.4, página 41):

O projeto NetBeans é o seguinte:

A página [index.html] é a seguinte:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title><h:outputText value="#{msg['welcome.titre']}" /></title>
    </h:head>
    <body>
      <h:form id="formulaire">
        <h:panelGrid columns="2">
          <p:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}" ajax="false"/>
          <p:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}" ajax="false"/>
        </h:panelGrid>
        <h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
        <p:commandLink value="#{msg['welcome.page1']}" action="page1" ajax="false"/>
      </h:form>
    </body>
  </f:view>
</html>

Nas linhas 15, 16 e 19, as tags <h:commandLink> foram substituídas por tags <p:commandLink>. Esta tag tem comportamento AJAX por predefinição, o que pode ser desativado definindo o atributo ajax="false". Assim, aqui, as tags <p:commandLink> comportam-se como tags <h:commandLink>: a página será recarregada quando estes links forem clicados.

5.6. Exemplo mv-pf-03: Layout de página utilizando Facelets

Este projeto demonstra a criação de páginas XHTML utilizando os modelos Facelets do exemplo [mv-jsf2-09] (secção 2.11):

 

O projeto NetBeans é o seguinte:

  • em [1], os ficheiros de configuração do projeto JSF,
  • em [2], as páginas XHTML,
  • em [3], o bean de suporte para a mudança de idioma,
  • em [4], os ficheiros de mensagens,
  • em [5], as dependências.

As páginas do projeto baseiam-se na página [layout.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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <f:view locale="#{changeLocale.locale}">
    <h:head>
      <title>JSF</title>
      <h:outputStylesheet library="css" name="styles.css"/>
    </h:head>
    <h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
      <h:form id="formulaire">
        <table style="width: 600px">
          <tr>
            <td colspan="2" bgcolor="#ccccff">
              <ui:include src="entete.xhtml"/>
            </td>
          </tr>
          <tr>
            <td style="width: 100px; height: 200px" bgcolor="#ffcccc">
              <ui:include src="menu.xhtml"/>
            </td>
            <td>
              <p:outputPanel id="contenu">
                <ui:insert name="contenu" >
                  <h2>Contenu</h2>
                </ui:insert>
              </p:outputPanel>
            </td>
          </tr>
          <tr bgcolor="#ffcc66">
            <td colspan="2">
              <ui:include src="basdepage.xhtml"/>
            </td>
          </tr>         
        </table>
      </h:form>
    </h:body>
  </f:view>
</html>
  • Linha 9: Uma tag <f:view> envolve toda a página para tirar partido da internacionalização que proporciona,
  • linha 15: um formulário com o ID do formulário. Este formulário constitui o corpo da página. Dentro deste corpo, existe apenas uma secção dinâmica, nas linhas 28–30. É aqui que a parte variável da página será inserida:
  • a área emoldurada acima será atualizada através de chamadas AJAX. Para a identificar, incluímo-la num contentor PrimeFaces gerado pela tag <p:outputPanel> (linha 27). E este contentor foi denominado `content` (atributo id). Uma vez que se encontra dentro de um formulário, que é ele próprio um contentor denominado `form`, o nome completo da área dinâmica é `form:content`. O primeiro : indica que partimos da raiz do documento, depois passamos para o contentor denominado «form» e, em seguida, para o contentor denominado «content». Um dos desafios do AJAX é nomear corretamente as áreas a serem atualizadas por uma chamada AJAX. A forma mais simples é consultar o código-fonte da página HTML recebida:

1
2
3
            <td><span id="formulaire:contenu">
                  <h2>Contenu</h2></span>
</td>

Acima, vemos que a tag <h:outputPanel> gerou uma tag HTML <span>. Neste exemplo, o nome relativo form:content (sem os dois pontos à esquerda) e o nome completo :form:content (com os dois pontos à esquerda) referem-se ao mesmo objeto.

Note que as chamadas AJAX (<p:commandButton>, <p:commandLink>) que atualizam a área dinâmica terão o atributo update=":form:content".

A página [index.xhtml] é a única página apresentada pelo projeto:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:fragment rendered="#{requestScope.page1 || requestScope.page2==null}">
        <ui:include src="page1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{requestScope.page2}">
        <ui:include src="page2.xhtml"/>
      </ui:fragment>
    </ui:define>
  </ui:composition>
</html>
  • Linha 8: O modelo para [index.xhtml] é a página [layout.xhtml] que acabámos de discutir.
  • Linha 9: esta é a área de ID de conteúdo que é atualizada por [index.html]. Nesta área, existem dois fragmentos:
    • o fragmento [page1.xhtml] na linha 11;
    • o fragmento [page2.xhtml] na linha 14.

Estes dois fragmentos são mutuamente exclusivos.

  • Linha 10: O fragmento [page1.xhtml] é exibido se a solicitação tiver o atributo page1 definido como true ou se o atributo page2 não existir. Este é o caso da primeira solicitação, em que nenhum destes atributos estará presente na solicitação. Neste caso, o fragmento [page1.xhtml] será exibido,
  • Linha 11: O fragmento [page2.xhtml] é exibido se a solicitação tiver o atributo page2 definido como true

O fragmento [page1.xhtml] é o seguinte:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <body>
    <h:panelGrid columns="2">
      <p:commandLink value="#{msg['page1.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" ajax="true" update=":formulaire:contenu"/>
      <p:commandLink value="#{msg['page1.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" ajax="true" update=":formulaire:contenu"/>
    </h:panelGrid>
    <h1><h:outputText value="#{msg['page1.titre']}" /></h1>
     <p:commandLink value="#{msg['page1.lien']}" update=":formulaire:contenu">
      <f:setPropertyActionListener value="#{true}" target="#{requestScope.page2}" />  
    </p:commandLink>
  </body>
</html>

e apresenta o seguinte conteúdo:

  • Linhas 11 e 12: os dois links para alterar o idioma. Estes dois links acionam chamadas AJAX (ajax=true). Este é o valor padrão. Portanto, não é necessário incluir o atributo ajax=true. Não o faremos no futuro. Note que estes dois links atualizam a área :form:content (atributo update), a que está destacada acima,
  • linha 15: um link de navegação AJAX que, mais uma vez, atualiza a área :form:content,
  • linha 16: utilizamos a tag <h:setPropertyActionListener> para definir o atributo «page2» na solicitação como «true». Isto fará com que o fragmento [page2.xhtml] (linha 6 abaixo) seja apresentado na página [index.xhtml]:

  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:fragment rendered="#{requestScope.page1 || requestScope.page2==null}">
        <ui:include src="page1.xhtml"/>
      </ui:fragment>
      <ui:fragment rendered="#{requestScope.page2}">
        <ui:include src="page2.xhtml"/>
      </ui:fragment>
    </ui:define>
</ui:composition>

O fragmento [page2.xhtml] é semelhante:

O código para [page2.xhtml] é o seguinte:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <body>
    <h1><h:outputText value="#{msg['page2.entete']}"/></h1>
    <p:commandLink value="#{msg['page2.lien']}" update=":formulaire:contenu">
      <f:setPropertyActionListener value="#{true}" target="#{requestScope.page1}" />  
    </p:commandLink>
  </body>
</html>

A partir deste exemplo, vamos ter em mente os seguintes pontos para mais tarde:

  • vamos utilizar o modelo [layout.xhtml] como modelo de página,
  • a área dinâmica será identificada pelo id:form:content e será atualizada através de chamadas AJAX.

5.7. Exemplo mv-pf-04: formulário de entrada

Este projeto é uma adaptação do projeto JSF2 [mv-jsf2-03] (ver secção 2.5):

O projeto NetBeans é o seguinte:

Acima, em [1], encontram-se as páginas XHTML do projeto. O layout é fornecido pelo modelo [layout.xhtml] discutido anteriormente. A página [index.xhtml] é a única página do projeto. É apresentada na área :form:content. O seu código é o seguinte:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <ui:include src="page1.xhtml"/>
    </ui:define>
  </ui:composition>
</html>

Apenas apresenta o fragmento [page1.xhtml]. Isto é equivalente ao formulário discutido no exemplo [mv-jsf2-03]. Recorde-se que o objetivo desse exemplo era apresentar as tags de entrada do JSF. Estas tags foram aqui substituídas por tags do PrimeFaces.

PanelGrid

Para formatar os elementos em [page1.xhtml], utilizamos a tag <p:panelGrid>. Por exemplo, para os dois links de idioma:


<!-- languages -->
    <p:panelGrid columns="2">
      <p:commandLink value="#{msg['form.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire:contenu"/>
      <p:commandLink value="#{msg['form.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire:contenu"/>
    </p:panelGrid>

Isto produz o seguinte resultado:

 

Outra forma da tag <p:panelGrid> é a seguinte:


<p:panelGrid>
 
      <f:facet name="header">  
        <p:row>  
          <p:column colspan="3"><h:outputText value="#{msg['form.titre']}"/></p:column>  
        </p:row>  
        <p:row>  
          <p:column><h:outputText value="#{msg['form.headerCol1']}"/></p:column>  
          <p:column><h:outputText value="#{msg['form.headerCol2']}"/></p:column>  
          <p:column><h:outputText value="#{msg['form.headerCol3']}"/></p:column>  
        </p:row>  
      </f:facet>        
 
      <p:row>
        <p:column>
          <h:outputText value="inputText"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputText" value="#{msg['form.loginPrompt']}" />  
          <p:inputText id="inputText" value="#{form.inputText}"/>
        </p:column>
        <p:column>
          <h:outputText id="inputTextValue" value="#{form.inputText}"/>
        </p:column>
      </p:row>
...
     <f:facet name="footer">
        <p:row>
          <p:column colspan="3">
            <div align="center">
              <p:commandButton value="#{msg['form.submitText']}" update=":formulaire:contenu"/>
            </div>
          </p:column>
        </p:row>
      </f:facet>    
</p:panelGrid>

As linhas e colunas da tabela são identificadas pelas tags <p:row> e <p:column>.

As linhas 3–12 definem o cabeçalho da tabela:

 

As linhas 14–25 definem uma linha da tabela:

 

As linhas 27–35 definem o rodapé da tabela:

 

inputText


      <p:row>
        <p:column>
          <h:outputText value="inputText"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputText" value="#{msg['form.loginPrompt']}" />  
          <p:inputText id="inputText" value="#{form.inputText}"/>
        </p:column>
        <p:column>
          <h:outputText id="inputTextValue" value="#{form.inputText}"/>
        </p:column>
</p:row>
 

palavra-passe


<p:row>
        <p:column>
          <h:outputText value="inputSecret"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputSecret" value="#{msg['form.passwdPrompt']}"/>
          <p:password id="inputSecret" value="#{form.inputSecret}" feedback="true"   
               promptLabel="#{msg['form.promptLabel']}" weakLabel="#{msg['form.weakLabel']}"  
               goodLabel="#{msg['form.goodLabel']}" strongLabel="#{msg['form.strongLabel']}" />  
        </p:column>
        <p:column>
          <h:outputText id="inputSecretValue" value="#{form.inputSecret}"/>
        </p:column>
      </p:row>

Linha 7: O atributo feedback=true fornece feedback sobre a força [1] da palavra-passe.

inputTextArea


<p:row>
        <p:column>
          <h:outputText value="inputTextArea"/>
        </p:column>
        <p:column>
          <h:outputLabel for="inputTextArea" value="#{msg['form.descPrompt']}"/>
          <p:editor id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
        </p:column> 
        <p:column>
          <h:outputText id="inputTextAreaValue" value="#{form.inputTextArea}"/>
        </p:column>
      </p:row>

Linha 7: A tag <p:editor> apresenta um editor de texto rico que permite formatar o texto (tipo de letra, tamanho, cor, alinhamento, etc.). O que é enviado para o servidor é o código HTML do texto introduzido [2].

selectOneListBox


<p:row>
        <p:column>
          <h:outputText value="selectOneListBox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneListBox1" value="#{msg['form.selectOneListBox1Prompt']}"/>
          <p:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}">
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
          </p:selectOneListbox>
        </p:column>
        <p:column>
          <h:outputText id="selectOneListBox1Value" value="#{form.selectOneListBox1}"/>
        </p:column>
      </p:row>
 

selectOneMenu


<p:row>
        <p:column>
          <h:outputText value="selectOneMenu"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneMenu" value="#{msg['form.selectOneMenuPrompt']}"/>
          <p:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
            <f:selectItem itemValue="4" itemLabel="quatre"/>
            <f:selectItem itemValue="5" itemLabel="cinq"/>
          </p:selectOneMenu>
        </p:column>
        <p:column>
          <h:outputText id="selectOneMenuValue" value="#{form.selectOneMenu}"/>
        </p:column>
      </p:row>
 

selectManyMenu


<p:row>
        <p:column>
          <h:outputText value="selectManyMenu"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectManyMenu" value="#{msg['form.selectManyMenuPrompt']}"/>
          <p:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
            <f:selectItem itemValue="1" itemLabel="un"/>
            <f:selectItem itemValue="2" itemLabel="deux"/>
            <f:selectItem itemValue="3" itemLabel="trois"/>
            <f:selectItem itemValue="4" itemLabel="quatre"/>
            <f:selectItem itemValue="5" itemLabel="cinq"/>
          </p:selectManyMenu>
          <p:commandLink value="#{msg['form.buttonRazText']}" actionListener="#{form.clearSelectManyMenu()}" update=":formulaire:selectManyMenu" style="margin-left: 10px"/>
        </p:column>
        <p:column>
          <h:outputText id="selectManyMenuValue" value="#{form.selectManyMenuValue}"/>
        </p:column>
      </p:row>
 

Linha 14: Note que o link [Reset] executa uma atualização AJAX no campo :form:selectManyMenu, que corresponde ao componente na linha 6. No entanto, é importante notar que, durante o POST AJAX, todos os valores do formulário são enviados. Por conseguinte, todo o modelo é atualizado. Com este modelo, no entanto, apenas o campo :form:selectManyMenu é atualizado.

selectBooleanCheckbox


<p:row>
        <p:column>
          <h:outputText value="selectBooleanCheckbox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectBooleanCheckbox" value="#{msg['form.selectBooleanCheckboxPrompt']}"/>
          <p:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
        </p:column>
        <p:column>
          <h:outputText id="selectBooleanCheckboxValue" value="#{form.selectBooleanCheckbox}"/>
        </p:column>
      </p:row>
 

selectManyCheckbox


<p:row>
        <p:column>
          <h:outputText value="selectManyCheckbox"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectManyCheckbox" value="#{msg['form.selectManyCheckboxPrompt']}"/>
          <p:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
            <f:selectItem itemValue="1" itemLabel="rouge"/>
            <f:selectItem itemValue="2" itemLabel="bleu"/>
            <f:selectItem itemValue="3" itemLabel="blanc"/>
            <f:selectItem itemValue="4" itemLabel="noir"/>
          </p:selectManyCheckbox>
        </p:column>
        <p:column>
          <h:outputText id="selectManyCheckboxValue" value="#{form.selectManyCheckboxValue}"/>
        </p:column>
      </p:row>
 

selectOneRadio


<p:row>
        <p:column>
          <h:outputText value="selectOneRadio"/>
        </p:column>
        <p:column>
          <h:outputLabel for="selectOneRadio" value="#{msg['form.selectOneRadioPrompt']}"/>
          <p:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}" >
            <f:selectItem itemValue="1" itemLabel="voiture"/>
            <f:selectItem itemValue="2" itemLabel="vélo"/>
            <f:selectItem itemValue="3" itemLabel="scooter"/>
            <f:selectItem itemValue="4" itemLabel="marche"/>
          </p:selectOneRadio>
        </p:column>
        <p:column>
          <h:outputText id="selectOneRadioValue" value="#{form.selectOneRadio}"/>
        </p:column>
      </p:row>
 

5.8. Exemplo: mv-pf-05: listas dinâmicas

Este projeto é uma adaptação do projeto JSF2 [mv-jsf2-04] (ver secção 2.6):

Image

Este projeto não introduz quaisquer novas tags PrimeFaces em comparação com o projeto anterior. Por conseguinte, não iremos comentá-lo. Está incluído na lista de exemplos disponíveis para o leitor no site do documento.

5.9. Exemplo: mv-pf-06: navegação – sessão – tratamento de exceções

Este projeto é uma adaptação do projeto JSF2 [mv-jsf2-05] (ver secção 2.7):

Mais uma vez, este exemplo não introduz quaisquer novas tags PrimeFaces. Iremos apenas comentar a tabela de links destacada acima:


<p:panelGrid columns="6">
  <p:commandLink value="1" action="form1?faces-redirect=true" ajax="false"/>
  <p:commandLink value="2" action="#{form.doAction2}" ajax="false"/>
  <p:commandLink value="3" action="form3?faces-redirect=true" ajax="false"/>
  <p:commandLink value="4" action="#{form.doAction4}" ajax="false"/>
  <p:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}" ajax="false"/>
  <p:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}" ajax="false"/>
</p:panelGrid>
  • Todos os links têm o atributo ajax=false. Por isso, a página carrega normalmente.
  • Observe as linhas 2 e 4 para saber como realizar um redirecionamento.

5.10. Exemplo: mv-pf-07: validação e conversão de dados de entrada

Este projeto é uma adaptação do projeto JSF2 [mv-jsf2-06] (ver secção 2.8):

Image

A aplicação introduz duas novas tags, a tag <p:messages>:


<p:messages globalOnly="true"/>

Image

e a tag <p:message>:


<p:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<p:message for="saisie1" styleClass="error"/>

Em comparação com a tag <h:message> do JSF, a tag <p:message> do PF introduz as seguintes alterações:

  • a aparência da mensagem de erro é diferente [1],
  • o campo de entrada incorreto é rodeado por uma moldura vermelha [2].

5.11. Exemplo: mv-pf-08: eventos relacionados com alterações no estado do componente

Este projeto é uma adaptação do projeto JSF2 [mv-jsf2-07] (ver secção 2.9):

Image

O projeto JSF introduziu o conceito de ouvintes. A gestão de ouvintes com o PrimeFaces era tratada de forma diferente.

Com o JSF:

1
2
3
4
5
6
7
        <!-- line 1 -->
        <h:outputText value="#{msg['combo1.prompt']}"/>
        <h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
        </h:selectOneMenu>
        <h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo1}"/>

Com o PrimeFaces:

<h:outputText value="#{msg['combo1.prompt']}"/>
        <p:selectOneMenu id="combo1" value="#{form.combo1}" styleClass="combo">
          <f:selectItems value="#{form.combo1Items}"/>
          <p:ajax update=":formulaire:combo2"/>  
        </p:selectOneMenu>
        <h:panelGroup></h:panelGroup>
        <h:outputText value="#{form.combo1}"/>

        <h:outputText value="#{msg['combo2.prompt']}"/>
        <p:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
        </p:selectOneMenu>
  • Linha 2: a tag <h:selectOneMenu> sem o atributo valueChangeListener,
  • linha 4: a tag <p:ajax> adiciona comportamento AJAX à sua tag pai <h:selectOneMenu>. Por predefinição, reage ao evento «value change» da lista combo1. Ao ocorrer este evento, os valores do formulário a que pertence serão enviados para o servidor através de uma chamada AJAX. O modelo é, portanto, atualizado. Utilizamos este novo modelo para atualizar a lista suspensa identificada como combo2 (linha 10). Note, na linha 4, que a chamada AJAX não executa quaisquer métodos do modelo. Isto é desnecessário aqui. Pretendemos simplesmente atualizar o modelo enviando os valores introduzidos.

5.12. Exemplo: mv-pf-09: Entrada Assistida

Este projeto apresenta tags de entrada específicas do PrimeFaces que facilitam a introdução de determinados tipos de dados:

5.12.1. O projeto NetBeans

O projeto NetBeans é o seguinte:

O valor do projeto reside em:

  • a única página [index.html] por ele apresentada,
  • o modelo [Form.java] para essa página.

5.12.2. O modelo

O formulário tem quatro campos de entrada associados ao seguinte modelo:


package forms;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
 
@ManagedBean
@SessionScoped
public class Form implements Serializable {
 
  private Date calendrier;
  private Integer slider = 100;
  private Integer spinner = 1;
  private String autocompleteValue;
 
  public Form() {
  }
 
  public List<String> autocomplete(String query) {
    ...
  }
  // getters and setters
...
}

As quatro entradas estão associadas aos campos nas linhas 14–17.

5.12.3. O formulário

O formulário é o seguinte:


<?xml version='1.0' encoding='UTF-8' ?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
 
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre']}"/></h2>
      <p:growl id="messages" autoUpdate="true"/>
      <p:panelGrid columns="3" columnClasses="col1,col2,col3,col4">
        <h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
        <h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
        <h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
 
        <!-- calendar -->
              ...
 
        <!-- slider -->
              ...
 
        <!-- spinner -->
              ...
 
        <!-- autocomplete -->
              ...
 
      </p:panelGrid>
    </ui:define>
  </ui:composition>
</html>

Vamos dar uma olhada nos quatro campos de entrada.

5.12.4. O calendário

A tag <p:calendar> permite-lhe selecionar uma data a partir de um calendário. Esta tag suporta vários atributos.


<h:outputText value="#{msg['calendar.prompt']}"/>
        <p:calendar id="calendrier" value="#{form.calendrier}" pattern="dd/MM/yyyy" timeZone="Europe/Paris"/>
        <h:outputText id="calendrierValue" value="#{form.calendrier}">
          <f:convertDateTime pattern="dd/MM/yyyy" type="date" timeZone="Europe/Paris"/>
        </h:outputText>

A linha 2 especifica que a data deve ser apresentada no formato «dd/mm/aaaa» e que o fuso horário é o de Paris. Quando coloca o cursor no campo de introdução, é apresentado um calendário:

 

5.12.5. O cursor

A tag <p:slider> permite introduzir um número inteiro arrastando um cursor ao longo de uma barra:

 

O código da tag é o seguinte:


        <h:outputText value="#{msg['slider.prompt']}"/>
        <h:panelGrid columns="1" style="margin-bottom:10px">  
          <p:inputText id="slider" value="#{form.slider}" required="true" requiredMessage="#{msg['slider.required']}" validatorMessage="#{msg['slider.invalide']}">  
            <f:validateLongRange minimum="100" maximum="200"/>
          </p:inputText>
          <p:slider for="slider" minValue="100" maxValue="200"/>  
        </h:panelGrid>  
<h:outputText id="sliderValue" value="#{form.slider}"/>
  • Linha 3: Esta é uma tag <p:inputText> padrão que permite introduzir um número inteiro. Este valor também pode ser introduzido utilizando o controlo deslizante,
  • linha 4: a tag <p:slider> está associada à tag de entrada <p:inputText> (através do atributo). Definimos um valor mínimo e máximo para ela.

5.12.6. O spinner

Já apresentámos este componente:


        <h:outputText value="#{msg['spinner.prompt']}"/>
        <p:spinner id="spinner" min="1" max="12" value="#{form.spinner}" required="true" requiredMessage="#{msg['spinner.required']}" validatorMessage="#{msg['spinner.invalide']}">
          <f:validateLongRange minimum="1" maximum="12"/>
        </p:spinner>
<h:outputText id="spinnerValue" value="#{form.spinner}"/>

Linha 3: O seletor permite introduzir um número inteiro entre 1 e 12. Pode introduzir o número diretamente no campo de entrada do seletor ou utilizar as setas para aumentar ou diminuir o número introduzido.

 

5.12.7. Preenchimento automático

O preenchimento automático consiste em digitar os primeiros caracteres do campo de entrada. Em seguida, aparecem sugestões numa lista suspensa. Pode selecionar uma delas. Este componente é utilizado em vez de listas suspensas quando estas contêm demasiado conteúdo. Suponha que pretende apresentar uma lista suspensa de cidades em França. Isso equivale a vários milhares de cidades. Se permitir que o utilizador digite os três primeiros caracteres do nome da cidade, pode então apresentar-lhe uma lista reduzida de cidades que começam por esses caracteres.

 

O código para este componente é o seguinte:


        <h:outputText value="#{msg['autocomplete.prompt']}"/>
        <p:autoComplete value="#{form.autocompleteValue}" completeMethod="#{form.autocomplete}" required="true" requiredMessage="#{msg['autocomplete.required']}"/>
        <h:outputText id="autocompleteValue" value="#{form.autocompleteValue}"/>
        <h:panelGroup/>
        <h:panelGroup>
        <center><p:commandLink value="#{msg['valider']}" update="formulaire:contenu"/></center>
        </h:panelGroup>
<h:panelGroup/>

A tag <p:autoComplete> na linha 2 é o que permite o preenchimento automático. O parâmetro de interesse aqui é o atributo completeMethod, cujo valor é o nome de um método no modelo responsável por gerar sugestões com base nos caracteres digitados pelo utilizador. Este método é definido da seguinte forma:


  public List<String> autocomplete(String query) {
    List<String> results = new ArrayList<String>();
 
    for (int i = 0; i < 10; i++) {
      results.add(query + i);
    }
 
    return results;
}

  • linha 1: o método recebe como parâmetro a sequência de caracteres digitada pelo utilizador no campo de entrada. Ele devolve uma lista de sugestões,
  • linhas 4–6: é construída uma lista de 10 sugestões, utilizando os caracteres recebidos como parâmetros e adicionando um dígito de 0 a 9 a cada um.

5.12.8. A tag <p:growl>

A tag <p:growl> é uma possível alternativa à tag <p:messages>, que exibe mensagens de erro de formulário.


      <p:growl id="messages" autoUpdate="true"/>

No exemplo acima, o atributo id não é utilizado. O atributo autoUpdate=true indica que a lista de mensagens de erro deve ser atualizada a cada envio (POST) do formulário.

Suponhamos que enviemos o seguinte formulário [1]:

  • Em [2], a tag <p:growl> exibe então as mensagens de erro associadas às entradas incorretas.

5.13. Exemplo: mv-pf-10: dataTable - 1

Este projeto apresenta a tag <p:dataTable>, que é utilizada para apresentar listas de dados

Image

5.13.1. O Projeto NetBeans

O projeto NetBeans é o seguinte:

O atrativo do projeto reside em:

  • a página única [index.html] apresentada pelo projeto,
  • o modelo [Form.java] para essa página e o bean [Person].

5.13.2. O ficheiro de mensagens

O ficheiro [messages_fr.properties] é o seguinte:


app.titre=intro-08
app.titre2=DataTable - 1
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom
layout.hautdepage=Primefaces en fran\u00e7ais
layout.menu=Menu fran\u00e7ais
layout.basdepage=ISTIA, universit\u00e9 d'Angers
form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.noData=La liste des personnes est vide
form.listePersonnes=Liste de personnes
form.action=Action

5.13.3. O modelo

O bean [Person] representa uma pessoa:


package forms;
 
import java.io.Serializable;
 
public class Personne implements Serializable{
  // data
  private int id;
  private String nom;
  private String prénom;
  
  // manufacturers
  public Personne(){
    
  }
  
  public Personne(int id, String nom, String prénom){
    this.id=id;
    this.nom=nom;
    this.prénom=prénom;
  }
  
  // toString
  public String toString(){
    return String.format("Personne[%d,%s,%s]", id,nom,prénom);
  }
  
  // getter and setters
...
}

O modelo para a página [index.xhtml] é a seguinte classe [Form]:


package forms;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
 
@ManagedBean
@SessionScoped
public class Form implements Serializable{
 
  // model
  private List<Personne> personnes;
  private int personneId;
 
  // manufacturer
  public Form() {
    // initialization of the list of persons
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }
 
  public void retirerPersonne() {
...
  }
  
  // getters and setters
...  
}
  • linhas 9-10: o bean tem âmbito de sessão,
  • linhas 18–24: o construtor cria uma lista de três pessoas, uma lista que persistirá entre pedidos,
  • linha 15: o ID de uma pessoa a ser removida da lista,
  • linhas 26–28: o método delete.

5.13.4. O formulário

O formulário é o seguinte [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:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}">
        <f:facet name="header">  
          #{msg['form.listePersonnes']}  
        </f:facet>  
        <p:column>
          <f:facet name="header">
            #{msg['personnes.headers.id']}
          </f:facet>
          #{personne.id}
        </p:column>
        <p:column>
          <f:facet name="header">
            #{msg['personnes.headers.nom']}
          </f:facet>
          #{personne.nom}
        </p:column>
        <p:column>
          <f:facet name="header">
            #{msg['personnes.headers.prenom']}
          </f:facet>
          #{personne.prénom}
        </p:column>
        <p:column>
          <f:facet name="header">
            #{msg['form.action']}
          </f:facet>
          <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu">
            <f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
          </p:commandLink>
        </p:column>
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>

Isto produz a seguinte visualização (emoldurada abaixo):

  • linha 12: gera a tabela apresentada na caixa acima. O atributo value especifica a coleção apresentada pela tabela, neste caso a lista de pessoas do modelo. O atributo emptyMessage é opcional. Especifica a mensagem a apresentar quando a lista estiver vazia. Por predefinição, é «nenhum registo encontrado». Aqui, será:
 
  • linhas 13–15: gerar cabeçalho [1],
  • linhas 16–21: gerar coluna [2],
  • linhas 22–27: gerar coluna [3],
  • linhas 28–33: gerar coluna [4],
  • linhas 34-41: gerar coluna [5].

O link [Remover] permite remover uma pessoa da lista. Na linha [38], o método [Form].removePerson executa esta tarefa. Ele precisa saber o ID da pessoa a ser removida. Este é fornecido na linha 39. Na linha 38, utilizámos o atributo action. Noutras ocasiões, utilizámos o atributo actionListener. Não tenho a certeza se compreendo totalmente a diferença funcional entre estes dois atributos. Na prática, no entanto, verificamos que os atributos definidos pelas tags <setPropertyActionListener> são definidos antes de o método designado pelo atributo action ser executado, ao passo que tal não acontece com o atributo actionListener. Em suma, assim que houver parâmetros a enviar para a ação chamada, deve utilizar o atributo action.

O método para remover uma pessoa é o seguinte:


...
@ManagedBean
@SessionScoped
public class Form implements Serializable{
 
  // model
  private List<Personne> personnes;
  private int personneId;
 
  public void retirerPersonne() {
    // search for the selected person
    int i = 0;
    for (Personne personne : personnes) {
      // current person = selected person?
      if (personne.getId() == personneId) {
        // delete the current person from the list
        personnes.remove(i);
        // we're done
        break;
      } else {
        // next person
        i++;
      }
    }
  }
...  
}

5.14. Exemplo: mv-pf-11: dataTable - 2

Este projeto apresenta uma tabela que exibe uma lista de dados onde é possível selecionar uma linha:

Ao selecionar uma linha na tabela, são enviadas informações sobre a linha selecionada para o modelo durante a solicitação POST. Como resultado, já não é necessário um link [Remover] para cada pessoa. Basta um único link para toda a tabela.

O projeto NetBeans é idêntico ao anterior, com algumas pequenas diferenças: o formulário e o seu modelo. O formulário [index.xhtml] é o seguinte:


<?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:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   rowKey="#{personne.id}"  selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
      <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu"/>
    </ui:define>
  </ui:composition>
</html>
  • linha 13: o atributo selectionMode permite escolher entre os modos de seleção única ou múltipla. Aqui, optámos por selecionar apenas uma única linha,
  • linha 13: o atributo rowkey designa um atributo dos elementos exibidos que permite que sejam selecionados de forma única. Aqui, escolhemos o ID da pessoa selecionada,
  • linha 13: o atributo selection designa o atributo do modelo que receberá uma referência à pessoa selecionada. Graças ao atributo rowkey anterior, é possível calcular uma referência à pessoa selecionada no lado do servidor. Não dispomos dos detalhes do método utilizado. Podemos imaginar que a coleção é percorrida sequencialmente em busca do elemento correspondente ao rowkey selecionado. Isto significa que, se o método que associa o rowkey à seleção for mais complexo, então este método não é utilizável,

Dito isto, o método [Form].removePerson evolui da seguinte forma:


...
 
@ManagedBean
@SessionScoped
public class Form implements Serializable {
 
  // model
  private List<Personne> personnes;
  private Personne personneChoisie;
 
  // manufacturer
  public Form() {
  ...
  }
 
  public void retirerPersonne() {
    // we remove the chosen person
    personnes.remove(personneChoisie);
  }
 
  // getters and setters
...
}
  • linha 9: em cada POST, a referência na linha 9 é inicializada com a referência, na lista da linha 8, da pessoa selecionada,
  • na linha 18: isto simplifica o processo de remoção da linha. A pesquisa que realizámos no exemplo anterior foi feita utilizando a tag <dataTable>.

5.15. Exemplo: mv-pf-12: dataTable - 3

Este projeto é semelhante ao anterior. A visualização é notavelmente idêntica:

Image

O projeto NetBeans é idêntico ao anterior, com algumas pequenas diferenças que iremos analisar. O formulário [index.xhtml] sofre as seguintes alterações:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
      <p:dataTable value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selectionMode="single" selection="#{form.personneChoisie}">
  ...
      </p:dataTable>
      <p:commandLink value="Retirer" action="#{form.retirerPersonne}" update=":formulaire:contenu"/>
    </ui:define>
  </ui:composition>
</html>
  • Linha 6: o atributo rowkey foi removido, mas o atributo selection permanece. A ligação entre os atributos rowkey e selection é agora estabelecida através de uma classe. O atributo value na linha 5 contém agora uma instância da interface PrimeFaces SelectableDataModel<T>. O método [Form].getPeople do modelo é atualizado da seguinte forma:

  public DataTableModel getPersonnes() {
    return new DataTableModel(personnes);
}

Assim, é adicionado um novo bean ao projeto:

Este bean é o seguinte:


package forms;
 
import java.util.List;
import javax.faces.model.ListDataModel;
import org.primefaces.model.SelectableDataModel;
 
public class DataTableModel extends ListDataModel<Personne> implements SelectableDataModel<Personne> {
 
  // manufacturers
  public DataTableModel() {
  }
 
  public DataTableModel(List<Personne> personnes) {
    super(personnes);
  }
 
  @Override
  public Object getRowKey(Personne personne) {
    return personne.getId();
  }
 
  @Override
  public Personne getRowData(String rowKey) {
    // list of persons
    List<Personne> personnes = (List<Personne>) getWrappedData();
    // the key is an integer 
    int key = Integer.parseInt(rowKey);
    // search for the selected person
    for (Personne personne : personnes) {
      if (personne.getId() == key) {
        return personne;
      }
    }
    // we found nothing
    return null;
  }
}
  • linha 7: a classe é uma instância da interface SelectableDataModel. Pelo menos duas classes implementam esta interface: ListDataModel, cujo construtor aceita uma lista como parâmetro, e ArrayDataModel, cujo construtor aceita uma matriz como parâmetro. Aqui, o nosso bean estende a classe ListDataModel,
  • linhas 13–15: o construtor recebe a lista de pessoas que gerimos como parâmetro. Este parâmetro é passado para a classe pai,
  • linha 18: o método getRowKey desempenha a função do atributo rowkey que foi removido. Deve devolver o objeto que identifica de forma única uma pessoa, neste caso o ID da pessoa,
  • linha 23: o método getRowData deve devolver o objeto selecionado com base na sua rowkey. Assim, neste caso, devolve uma pessoa com base no seu ID. A referência obtida desta forma será atribuída ao objeto de destino do atributo selection na tag dataTable, neste caso o atributo selection="#{form.personneChoisie}". O parâmetro do método é a rowkey do objeto selecionado pelo utilizador, na forma de uma string,
  • linhas 24–35: devolvem a referência à pessoa cujo ID foi recebido. Esta referência será atribuída ao modelo [Form].personneChoisie. O método [retirerPersonne] permanece, portanto, inalterado:

  public void retirerPersonne() {
    // on enlève la personne choisie
    personnes.remove(personneChoisie);
  }

Esta é a técnica a utilizar quando a relação entre os atributos **rowkey e **selection não é uma simples relação de propriedade-objeto (**rowkey** para **selection**).

5.16. Exemplo: mv-pf-13: dataTable - 4

Este projeto é semelhante ao anterior, exceto que o método para selecionar a pessoa a ser removida muda:

Image

Acima, vemos que o objeto é selecionado através de um menu de contexto (clique com o botão direito do rato). É solicitada uma confirmação da eliminação:

 

O formulário [index.xhtml] altera-se da seguinte forma:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
 
      <!-- title -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
 
      <!-- contextual menu -->
      <p:contextMenu for="personnes">  
        <p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
      </p:contextMenu>  
 
      <!-- dialog box -->
      <p:confirmDialog widgetVar="confirmation" message="#{msg['form.suppression.confirmation']}"  
                       header="#{msg['form.suppression.message']}" severity="alert" >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu"                          action="#{form.retirerPersonne}" oncomplete="confirmation.hide()"/>
        <p:commandButton value="#{msg['form.supprimer.non']}" onclick="confirmation.hide()" type="button" />                
      </p:confirmDialog>  
 
      <!-- dataTable-->
      <p:dataTable id="personnes" value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • linhas 9–11: definem um menu de contexto para (atributo for) a dataTable na linha 21 (atributo id). Este menu de contexto aparece quando clica com o botão direito do rato na tabela de pessoas,
  • linha 10: o nosso menu tem apenas uma opção (tag menuItem). Quando esta opção é clicada, o código JavaScript no atributo onclick é executado. O código JavaScript [confirmation.show()] exibe a caixa de diálogo da linha 14 (atributo widgetVar). É o seguinte:
  • linha 14: o atributo «message» exibe [3], o atributo «header» exibe [1], o atributo «severity» exibe o ícone [2],
  • linha 16: exibe [4]. Ao clicar, a pessoa é eliminada (atributo action), e a caixa de diálogo é fechada (atributo oncomplete). O atributo oncomplete é código JavaScript que é executado assim que a ação do lado do servidor tiver sido executada,
  • linha 17: exibe [5]. Quando clicado, a caixa de diálogo fecha e a pessoa não é eliminada.

5.17. Exemplo: mv-pf-14: dataTable - 5

Este projeto demonstra que é possível receber uma resposta do servidor após a execução de uma chamada AJAX. Para tal, utilizamos o atributo oncomplete da chamada AJAX:

 

O formulário [index.xhtml] é alterado da seguinte forma:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
...
<!-- dialog box 1 -->
      <p:confirmDialog widgetVar="confirmation" ... >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu" action="#{form.retirerPersonne}" oncomplete="handleRequest(xhr, status, args);confirmation.hide()"/>
        <p:commandButton ... />                
      </p:confirmDialog>  
 
      <!-- Javascript -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // erreur ?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
      </script> 
...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • linha 7: o atributo oncomplete chama a função JavaScript nas linhas 13–18,
  • linha 13: a assinatura do método deve ser esta. args é um dicionário que o modelo do lado do servidor pode preencher,
  • linha 15: verificamos se o dicionário args tem um atributo chamado «msgError». Se tiver, é exibido (linha 16).

No modelo, o método [removePerson] evolui da seguinte forma:


public void retirerPersonne() {
    // suppression aléatoire
    int i = (int) (Math.random() * 2);
    if (i == 0) {
      // on enlève la personne choisie
      personnes.remove(personneChoisie);
    } else {
      // on renvoie une erreur
      String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
      RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
    }
  }
  • linha 3: gerar um número aleatório 0 ou 1,
  • linhas 4–6: se for 0, a pessoa selecionada pelo utilizador é removida da lista de pessoas,
  • linha 9: caso contrário, é criada uma mensagem de erro internacionalizada:

form.msgErreur=La personne n'a pu \u00eatre supprim\u00e9e. Veuillez r\u00e9essayer ult\u00e9rieurement.
form.msgErreur_detail=La personne n'a pu \u00eatre supprim\u00e9e. Veuillez r\u00e9essayer ult\u00e9rieurement.
  • linha 10: uma instrução complexa que adiciona o atributo denominado «msgError» ao dicionário args que mencionámos anteriormente, com o valor de msgError construído na linha 9. Este atributo é então recuperado pelo método JavaScript em [index.xhtml]:

      <!-- Javascript -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // erreur ?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
</script> 

5.18. Exemplo: mv-pf-15: a barra de ferramentas

Neste projeto, estamos a criar uma barra de ferramentas:

A barra de ferramentas é o componente apresentado na caixa acima. É criada utilizando o seguinte código XHTML [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:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <!-- title -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
 
      <!-- toolbar-->
      <p:toolbar>
        <p:toolbarGroup align="left">  
          ...  
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          ...  
        </p:toolbarGroup>  
      </p:toolbar>
    </ui:define>
  </ui:composition>
</html>
  • linhas 15–22: a barra de ferramentas,
  • linhas 16–18: definem o grupo de componentes à esquerda da barra de ferramentas,
  • linhas 19-21: o mesmo para os componentes à direita.

Os componentes à esquerda da barra de ferramentas são os seguintes:


        <p:toolbarGroup align="left">  
          <h:outputText value="#{msg['form.etudiant']}"/>
          <p:spacer width="50px"/>
          <p:selectOneMenu value="#{form.personneId}" effect="fade">  
            <f:selectItems value="#{form.personnes}" var="personne" itemLabel="#{personne.prénom} #{personne.nom}" itemValue="#{personne.id}"/>  
          </p:selectOneMenu>              
          <p:separator/>
          <p:commandButton id="delete-personne" icon="ui-icon-trash" action="#{form.supprimerPersonne}" update=":formulaire:contenu"/>  
          <p:tooltip for="delete-personne" value="#{msg['form.delete.personne']}"/>  
</p:toolbarGroup>

Eles apresentam a vista abaixo:

  • linha 2: exibe [1],
  • linha 3: exibe um espaço de 30 píxeis [2],
  • linhas 4–6: exibem uma lista suspensa com uma lista de pessoas [3],
  • linha 7: exibe um separador [4],
  • linha 8: exibe um botão [5] utilizado para eliminar a pessoa selecionada da lista suspensa. O botão possui um ícone. Estes ícones são do jQuery UI. Pode encontrar uma lista dos mesmos no URL [http://jqueryui.com/themeroller/] [6]:
  • para encontrar o nome de um ícone, basta passar o rato por cima dele. Este nome é então utilizado no atributo icon do componente <commandButton>, por exemplo icon="ui-icon-trash". Note que acima, o nome atribuído é .ui-icon-trash e que o ponto inicial é removido deste nome no atributo icon,
  • Linha 9: cria uma dica de ferramenta para o botão (atributo for). Quando passa o cursor sobre o botão, a mensagem da dica de ferramenta aparece [7].

O modelo associado a estes componentes é o seguinte:


package forms;
 
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
 
@ManagedBean
@SessionScoped
public class Form implements Serializable {
 
  // model
  private List<Personne> personnes;
  private int personneId;
 
  // manufacturer
  public Form() {
    // initialization of the list of persons
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }
 
  public void supprimerPersonne() {
    // search for the selected person
    int i = 0;
    for (Personne personne : personnes) {
      // current person = selected person?
      if (personne.getId() == personneId) {
        // delete the current person from the list
        personnes.remove(i);
        // we're done
        break;
      } else {
        // next person
        i++;
      }
    }
  }
 
  // getters and setters
  ...
}

Os componentes no lado direito da barra de ferramentas são os seguintes:


<p:toolbar>
        <p:toolbarGroup align="left">  
          ... 
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          <p:menuButton value="#{msg['form.options']}">  
            <p:menuitem id="menuitem-francais" value="#{msg['form.francais']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire"/>  
            <p:menuitem id="menuitem-anglais" value="#{msg['form.anglais']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire"/>  
          </p:menuButton>  
        </p:toolbarGroup>  
      </p:toolbar>

Eles exibem a vista abaixo:

 
  • linhas 6–9: um botão de menu. Contém opções de menu,
  • linha 7: a opção para mudar o formulário para francês,
  • linha 8: a opção para mudar para inglês.

5.19. Conclusão

Agora sabemos o suficiente para portar a nossa aplicação de exemplo para o PrimeFaces. Abordámos apenas cerca de quinze componentes, enquanto a biblioteca contém mais de 100. Encorajamos os leitores a pesquisar quaisquer componentes em falta diretamente no site do PrimeFaces.

5.20. Testes com o Eclipse

Os projetos Maven estão disponíveis no site de exemplos [1]:

Depois de importados para o Eclipse, podem ser executados [2]. Selecione Tomcat em [3]. Serão então apresentados no navegador interno do Eclipse [3].