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 analisámos no início deste documento:
![]() |
As páginas JSF foram construídas com três bibliotecas de tags:
- linha 2: as balizas <h:x> do espaço de nomes [http://java.sun.com/jsf/html] que correspondem às balizas HTML,
- linha 3: as tags <f:y> do espaço de nomes [http://java.sun.com/jsf/core], que correspondem às tags JSF,
- linha 4: as tags <ui:z> do espaço de nomes [http://java.sun.com/jsf/facelets], que correspondem às tags dos facelets.
Para criar as páginas JSF, vamos adicionar uma quarta biblioteca de tags, a dos componentes Primefaces.
- linha 3: as tags <p:z> do espaço de nomes [http://primefaces.org/ui] correspondem aos componentes Primefaces.
Esta é a única alteração que irá aparecer. Por isso, aparece nas vistas. Os gestores de eventos e os modelos permanecem tal como eram com JSF. Este é um ponto importante a compreender.
A utilização dos componentes Primefaces permite criar interfaces web mais intuitivas, graças aos inúmeros componentes desta biblioteca, e mais fluidas, graças à tecnologia AJAX que utiliza nativamente. Fala-se, então, de interfaces ricas ou RIA (Rich Internet Application).
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] apresenta a lista de componentes que podem ser utilizados numa página PF:
![]() |
Nos exemplos que se seguem, iremos utilizar as duas primeiras funcionalidades do Primefaces:
- alguns dos cerca de cem componentes disponíveis,
- o comportamento nativo AJAX dos mesmos.
Entre os componentes disponíveis:
![]() | ![]() | ![]() |
Iremos utilizar apenas cerca de quinze deles nos nossos exemplos, mas isso será suficiente para compreender os princípios de construção de uma página Primefaces.
5.3. Aprender a utilizar o Primefaces
O Primefaces disponibiliza exemplos de utilização de cada um dos seus componentes. Basta clicar no respetivo link. 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].
Temos aqui três novidades:
- o componente [Spinner], que não existe por defeito no JSF,
- o mesmo se aplica à caixa de diálogo,
- por fim, o POST, acionado pelo [Submit], utiliza AJAX. Se observarmos atentamente o navegador durante o POST, não vemos a ampulheta. A página não é recarregada. É simplesmente alterada: um novo componente, neste caso a caixa de diálogo, aparece na página.
Vamos ver como tudo isto acontece. O código XHTML do 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>
Em primeiro lugar, reparemos que encontramos as balizas JSF clássicas: <h:form> na linha 1, <h:panelGrid> na linha 3, <h:outputLabel> na linha 4. Algumas balizas JSF são retomadas por PF e enriquecidas: <p:commandButton> linha 21. Em seguida, encontram-se as balizas PF de formatação: <p:panel> linha 2, <p:outputPanel> linha 13, <p:dialog> linha 23. Por fim, temos as balizas de introdução de dados: <p:spinner> linha 5.
Vamos analisar este código em correspondência com a vista:
![]() |
- em [1], o componente obtido com a baliza <p:panel> da linha 2,
- em [2], o campo de introdução de dados obtido pela combinação das balizas <p:outputLabel> e <p:spinner>, linhas 6 e 7,
- em [3], o botão do POST obtido com a baliza <p:commandButton> da linha 21,
- em [4], a caixa de diálogo das linhas 23-25,
- em [5], um contentor invisível para dois componentes. É criado pela baliza <p:outputPanel> da 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: apresenta o texto [1]. É, ao mesmo tempo, um rótulo para o componente id=ajaxspinner (atributo «for»). Este componente é o da linha 3 (atributo «id»),
- linhas 3-5: apresentam o componente [2]. Este componente é um componente de introdução/visualização associado ao modelo #{spinnerController.number5} (atributo «value»),
- linha 6: apresenta o componente [3]. Este componente é um componente de visualização ligado ao modelo #{spinnerController.number5} (atributo value),
- linha 4: a baliza <p:ajax> adiciona um comportamento AJAX ao spinner. Sempre que este altera o seu valor, é efetuada uma chamada ao POST com esse valor (atributo process="@this") para o modelo #{spinnerController.number5}. Feito isto, é efetuada uma atualização da página (atributo update). Este atributo tem como valor o id de um componente da página, neste caso o da linha 6. O componente alvo do atributo update é então atualizado com o modelo. Este é novamente #{spinnerController.number5}, ou seja, o valor de spinner. Assim, a zona [3] segue as entradas da zona [2].
Trata-se aqui de um comportamento AJAX, acrónimo que significa Asynchronous Javascript And XML. De um modo geral, um comportamento AJAX é o seguinte:
![]() |
- o navegador apresenta uma página HTML que contém código JavaScript (J de AJAX). Os elementos da página formam um objeto JavaScript denominado DOM (Document Object Model),
- o servidor hospeda a aplicação web que produziu esta página,
- em [1], ocorre um evento na página. Por exemplo, o incremento do spinner. Este evento é gerido por JavaScript,
- No [2], o JavaScript executa um POST na aplicação web. Faz-o de forma assíncrona (o A de AJAX). O utilizador pode continuar a trabalhar com a página. Esta não fica bloqueada, mas pode ser bloqueada, se necessário. O POST atualiza o modelo da página a partir dos valores enviados, neste caso o modelo #{spinnerController.number5},
- em [3]; a aplicação web devolve ao JavaScript uma resposta XML (o X de AJAX) ou JSON (JavaScript na notação de objeto),
- em [4], o JavaScript utiliza esta resposta para atualizar uma área específica do DOM, neste caso a área de id=ajaxspinnervalue.
Quando se utiliza o JSF e o Primefaces, o JavaScript é gerado pelo Primefaces. Esta biblioteca baseia-se na biblioteca JavaScript JQuery. Da mesma forma, os componentes do Primefaces baseiam-se nos da biblioteca de componentes JQuery e UI (Interface do Utilizador). Portanto, o JQuery constitui a base do Primefaces.
Voltemos ao nosso exemplo e apresentemos agora o 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 POST é acionado pelo botão da linha 1. No Primefaces, as tags que acionam um POST fazem-no, por predefinição, sob a forma de uma chamada AJAX. É por isso que estas tags têm um atributo «update» para indicar a área a atualizar, assim que a resposta do servidor for recebida. Aqui, a área atualizada é o panelGrid da linha 4. Assim, quando o POST for devolvido, esta área será atualizada com os valores enviados para o modelo. No entanto, estas encontram-se dentro de uma caixa de diálogo que, por predefinição, não está visível. É o atributo «oncomplete» da linha 1 que a exibe. Este evento ocorre no final do processamento do POST. O valor deste atributo é código JavaScript. Aqui, exibe-se a caixa de diálogo com o id=dialog, ou seja, a da linha 3 (atributo widgetVar),
- linha 3: vemos vários atributos da caixa de diálogo. É preciso experimentar para ver o que fazem.
Já falámos do modelo, mas ainda não o apresentámos. É este:
De um modo geral, pode-se proceder da seguinte forma:
- identificar o componente Primefaces que se pretende utilizar,
- analisar o seu exemplo. Os exemplos do Primefaces são bem elaborados 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]: criamos um projeto Maven do tipo [Web Application],
![]() |
- [4]: o servidor será o Tomcat,
- no [5], o projeto gerado,
- em [6], remove-se o ficheiro [index.jsp] e o pacote Java,
![]() |
- em [7, 8]: nas propriedades do projeto, adiciona-se suporte para o Java Server Faces,
![]() |
- para [9]; no separador [Components], seleciona-se a biblioteca de componentes PrimeFaces. O NetBeans oferece suporte a outras bibliotecas de componentes: ICEFaces e RichFaces.
- em [10], o projeto gerado. Em [11], repare-se na dependência do Primefaces.
Em suma, um projeto Primefaces é um projeto JSF clássico ao qual foi adicionada uma dependência do Primefaces. Nada mais.
Tendo compreendido isto, alteramos o ficheiro [pom.xml] para trabalhar 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>
Nas linhas 26-30, repare no repositório Maven para o Primefaces. Feitas estas alterações, compilamos o projeto para iniciar o download das dependências. Obtemos 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>
<!-- formulário -->
<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()" />
<!-- caixa de diálogo -->
<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 nos esqueçamos da linha 5, que declara o espaço de nomes da biblioteca de tags do Primefaces. Adicionamos ao projeto o bean que serve de modelo para a página:
![]() |
O bean é o seguinte:
package beans;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class SpinnerController {
// modelo
private int number1;
private double number2;
private int number3;
private int number4;
private int number5;
// getters e setters
...
}
A classe é um bean (linha 6) com âmbito de consulta (linha 7). Como não foi indicado nenhum nome, o bean recebe o nome da classe com a primeira letra em minúscula: spinnerController.
Ao executar o projeto, obtém-se o seguinte:
![]() |
Acabámos assim de mostrar como testar um exemplo retirado do site do Primefaces. Todos os exemplos podem ser testados desta forma.
A seguir, iremos concentrar-nos apenas em determinados componentes do Primefaces. Em primeiro lugar, retomaremos os exemplos estudados com JSF e substituiremos algumas tags JSF por tags do Primefaces. O aspeto das páginas será ligeiramente alterado; estas terão um comportamento AJAX, mas os beans associados não terão de ser alterados. Em cada um dos exemplos que se seguem, limitar-nos-emos a apresentar o código XHTML das páginas e as capturas de ecrã associadas. Convidamos o leitor 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 entre páginas
Este projeto é a adaptação do projeto JSF [mv-jsf2-02] (parágrafo 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 balizas <h:commandLink> foram substituídas por balizas <p:commandLink>. Esta baliza tem um comportamento AJAX por predefinição, que pode ser desativado definindo o atributo ajax="false". Assim, neste caso, as tags <p:commandLink> comportam-se como tags <h:commandLink>: a página será recarregada ao clicar nestes links.
5.6. Exemplo mv-pf-03: formatação com facelets
Este projeto apresenta a criação de páginas XHTML utilizando os modelos facelets do exemplo [mv-jsf2-09] (parágrafo 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 idiomas,
- em [4], os ficheiros de mensagens,
- em [5], as dependências.
As páginas do projeto têm como modelo a 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 baliza <f:view> enquadra toda a página para tirar partido da internacionalização que esta permite,
- linha 15: um formulário com o ID «formulário». Este formulário constitui o corpo da página. Neste corpo, existe apenas uma parte dinâmica, a das linhas 28-30. É aí que será inserida a parte variável da página:
![]() |
- a área emoldurada acima será atualizada por chamadas AJAX. Para a identificar, incluímo-la num contentor Primefaces gerado pela baliza <p:outputPanel> (linha 27). E este contentor foi denominado «conteúdo» (atributo id). Como se encontra num formulário que, por sua vez, é um contentor denominado «formulário», o nome completo da área dinâmica é:formulário:conteúdo. O primeiro «:» indica que se parte da raiz do documento, passando depois pelo contentor denominado «formulário» e, em seguida, pelo contentor denominado «conteúdo». Uma dificuldade com o AJAX é nomear corretamente as áreas a atualizar através de uma chamada ao AJAX. O mais simples é consultar o código-fonte da página HTML recebida:
Acima, vemos que a baliza <h:outputPanel> gerou uma baliza HTML <span>. Neste exemplo, o nome relativo «formulário:conteúdo» (sem o «:» inicial) e o nome completo «:formulário:conteúdo» (com o «:» inicial) designam o mesmo objeto.
É importante notar que as chamadas AJAX (<p:commandButton>, <p:commandLink>) que atualizam a zona dinâmica terão o atributo update=":formulário:conteúdo".
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>
- na linha 8, o modelo de [index.xhtml] é a página [layout.xhtml] que acabámos de apresentar,
- na linha 9, é a zona de ID do conteúdo que é atualizada pelo [index.html]. Nesta zona, existem dois fragmentos:
- o fragmento [page1.xhtml], linha 11;
- o fragmento [page2.xhtml], linha 14.
Estes dois fragmentos são mutuamente exclusivos.
- linha 10: o fragmento [page1.xhtml] é apresentado se a consulta tiver o atributo page1 definido como true ou se o atributo page2 não existir. É o caso da primeira consulta, em que nenhum destes atributos estará presente na consulta. Nesse caso, será exibido o fragmento [page1.xhtml],
- linha 11; o fragmento [page2.xhtml] é apresentado se a consulta 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 provocam chamadas AJAX (ajax=true). Esta é a configuração por predefinição. Por isso, não é necessário definir o atributo ajax=true. Não o faremos mais daqui em diante. Note-se que estes dois links atualizam a área :formulário:conteúdo (atributo update), a que está destacada acima,
- linha 15: um link de navegação AJAX que, mais uma vez, atualiza a zona :formulário:conteúdo,
- linha 16: utiliza-se a baliza <h:setPropertyActionListener> para incluir o atributo page2 na consulta com o valor true. Isto terá como efeito a exibição do fragmento [page2.xhtml] (linha 6 abaixo) 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 de [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>
Deste exemplo, reteremos os seguintes pontos para a continuação:
- utilizaremos o modelo [layout.xhtml] como modelo das páginas,
- a zona dinâmica será identificada pelo id:formulário:conteúdo e será atualizada através de chamadas AJAX.
5.7. Exemplo mv-pf-04: formulário de introdução de dados
Este projeto é a adaptação do projeto JSF2 [mv-jsf2-03] (ver parágrafo 2.5):
![]() |
O projeto NetBeans é o seguinte:
![]() |
Acima, em [1], encontram-se as páginas XHTML do projeto. A formatação é assegurada pelo modelo [layout.xhtml] analisado anteriormente. A página [index.xhtml] é a única página do projeto. É apresentada na área :formulário:conteúdo. 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>
Limita-se a apresentar o fragmento [page1.xhtml]. Este é equivalente ao formulário analisado no exemplo [mv-jsf2-03]. Recorde-se que o objetivo deste último era apresentar as balizas de introdução de dados JSF. Estas balizas foram aqui substituídas por balizas Primefaces.
PanelGrid
Para formatar os elementos de [page1.xhtml], utilizamos a baliza <p:panelGrid>. Por exemplo, para os dois links dos idiomas:
<!-- idiomas -->
<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>
O resultado é o seguinte:
Outra forma da baliza <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 balizas <p:row> e <p:column>.
As linhas 3 a 12 definem o cabeçalho da tabela:
As linhas 14 a 25 definem uma linha da tabela:
As linhas 27 a 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>
password
<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>
![]() |
Na linha 7, o atributo feedback=true permite obter feedback sobre a qualidade 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>
![]() |
Na linha 7, a baliza <p:editor> apresenta um editor avançado 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>
![]() |
Na linha 14, note-se que a ligação [Raz] efetua uma atualização AJAX da zona :formulário:selectManyMenu, que corresponde ao componente da linha 6. No entanto, é importante saber que, durante o POST AJAX, todos os valores do formulário são enviados. Por isso, é todo o modelo que é atualizado. Com este modelo, contudo, apenas se atualiza a zona :formulário:selectManyMenu.
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 é a adaptação do projeto JSF2 [mv-jsf2-04] (ver parágrafo 2.6):

Este projeto não introduz novas balizas PrimeFaces em relação ao projeto anterior. Por isso, não o iremos comentar. Faz parte da lista de exemplos disponibilizados ao leitor no site do documento.
5.9. Exemplo: mv-pf-06: navegação – sessão – gestão de exceções
Este projeto é a adaptação do projeto JSF2 [mv-jsf2-05] (ver parágrafo 2.7):
![]() |
Mais uma vez, este exemplo não introduz novas balizas Primefaces. Limitar-nos-emos a comentar a tabela de ligações emoldurada 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, ocorre um carregamento normal da página,
- reparem nas linhas 2 e 4, onde se mostra como efetuar um redirecionamento.
5.10. Exemplo: mv-pf-07: validação e conversão dos dados introduzidos
Este projeto é a adaptação do projeto JSF2 [mv-jsf2-06] (ver parágrafo 2.8):

A aplicação introduz duas novas balizas, a baliza <p:messages>:
<p:messages globalOnly="true"/>

e a baliza <p:message>:
<p:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<p:message for="saisie1" styleClass="error"/>
Em comparação com a baliza <h:message> de JSF, a baliza <p:message> de PF introduz as seguintes alterações:
- a aparência da mensagem de erro é diferente em [1],
- a área de introdução de dados incorreta está rodeada por uma moldura vermelha no [2].
5.11. Exemplo: mv-pf-08: eventos relacionados com a alteração do estado dos componentes
Este projeto é a adaptação do projeto JSF2 [mv-jsf2-07] (ver parágrafo 2.9):

O projeto JSF introduziu o conceito de listeners. A gestão do listener com o Primefaces foi realizada de forma diferente.
Com o JSF:
Com o Primefaces:
- linha 2: a baliza <h:selectOneMenu> sem o atributo valueChangeListener,
- linha 4: a baliza <p:ajax> adiciona um comportamento AJAX à sua baliza pai <h:selectOneMenu>. Por predefinição, reage ao evento «alteração de valor» da lista combo1. Neste evento, os valores do formulário ao qual pertence serão enviados para o servidor através de uma chamada AJAX. O modelo é, assim, atualizado. Utiliza-se este novo modelo para atualizar a lista suspensa identificada por combo2 (linha 10). Note-se, na linha 4, que a chamada AJAX não executa nenhum método do modelo. Isso é desnecessário neste caso. Pretende-se simplesmente alterar o modelo através do POST com base nos valores introduzidos.
5.12. Exemplo: mv-pf-09: introdução assistida
Este projeto apresenta campos de introdução de dados específicos do Primefaces que facilitam a introdução de determinados tipos de dados:
![]() |
5.12.1. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
O interesse do projeto reside na:
- na única página [index.html] apresentada pelo mesmo,
- o modelo [Form.java] dessa página.
5.12.2. O modelo
O formulário apresenta quatro campos de introdução de dados 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 e setters
...
}
As quatro entradas estão associadas aos campos das linhas 14 a 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"/>
<!-- calendário -->
...
<!-- slider -->
...
<!-- spinner -->
...
<!-- preenchimento automático -->
...
</p:panelGrid>
</ui:define>
</ui:composition>
</html>
Vamos analisar os quatro campos de preenchimento.
5.12.4. O calendário
A baliza <p:calendar> permite selecionar uma data a partir de um calendário. Esta baliza admite 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>
Na linha 2, indica-se que a data deve ser apresentada no formato «dd/mm/aaaa» e que o fuso horário é o de Paris. Quando se coloca o cursor na área de introdução de dados, é apresentado um calendário:
![]() |
5.12.5. O controlo deslizante
A baliza <p:slider> permite introduzir um número inteiro arrastando um cursor ao longo de uma barra:
O código da baliza é 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: trata-se de uma baliza <p:inputText> clássica que permite introduzir o número inteiro. Este também pode ser introduzido através do slider,
- linha 4: a tag <p:slider> está associada à tag de introdução de dados <p:inputText> (atributo «for»). Define-se um valor mínimo e um valor máximo para a mesma.
5.12.6. O spinner
Já tivemos oportunidade de apresentar 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 spinner permite a introdução de um número inteiro entre 1 e 12. É possível introduzir o número diretamente na área de introdução do spinner ou utilizar as setas para aumentar ou diminuir o número introduzido.
5.12.7. A preenchimento assistido
A introdução assistida consiste em digitar os primeiros caracteres do valor a introduzir. Surgem então sugestões numa lista suspensa. É possível selecionar uma delas. Este componente é utilizado em vez das listas suspensas quando estas têm um conteúdo demasiado extenso. Suponhamos que se pretenda apresentar uma lista suspensa das cidades de França. São vários milhares de cidades. Se se permitir que o utilizador digite os três primeiros caracteres do nome da cidade, é possível apresentar-lhe uma lista reduzida das cidades que começam por esses caracteres.
![]() |
O código deste 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 baliza <p:autoComplete> na linha 2 é a que permite a introdução assistida. O parâmetro que nos interessa aqui é o atributo completeMethod, cujo valor é o nome de um método do modelo, responsável por apresentar sugestões correspondentes aos caracteres digitados pelo utilizador. Este método é, neste caso, o seguinte:
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 cadeia de caracteres digitados pelo utilizador na zona de introdução de dados. Devolve uma lista de sugestões,
- linhas 4-6: constrói-se uma lista de 10 sugestões que inclui os caracteres recebidos como parâmetros e lhes acrescenta um algarismo de 0 a 9.
5.12.8. A baliza <p:growl>
A baliza <p:growl> é uma alternativa possível à baliza <p:messages>, que exibe as mensagens de erro do 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 POST do formulário.
Suponhamos que se valide o seguinte formulário [1]:
![]() |
- em [2], a baliza <p:growl> exibe então as mensagens de erro associadas aos dados introduzidos incorretamente.
5.13. Exemplo: mv-pf-10: dataTable - 1
Este projeto apresenta a baliza <p:dataTable>, que serve para apresentar listas de dados

5.13.1. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
O interesse do projeto reside na:
- na única página [index.html] apresentada pelo mesmo,
- no modelo [Form.java] dessa página e no bean [Personne].
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 [Personne] representa uma pessoa:
package forms;
import java.io.Serializable;
public class Personne implements Serializable{
// dados
private int id;
private String nom;
private String prénom;
// fabricantes
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);
}
// getters e setters
...
}
O modelo da 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{
// modelo
private List<Personne> personnes;
private int personneId;
// construtor
public Form() {
// inicialização da lista de pessoas
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 e setters
...
}
- linhas 9-10: o bean tem alcance de sessão,
- linhas 18-24: o construtor cria uma lista de três pessoas, lista essa que irá, portanto, persistir ao longo das solicitações,
- linha 15: o número de uma pessoa a eliminar da lista,
- linhas 26-28: o método de eliminação.
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 gera a seguinte visualização (requadro abaixo):
![]() |
- linha 12: gera a tabela destacada acima. O atributo «value» indica a coleção apresentada pela tabela, neste caso, a lista de pessoas do modelo. O atributo emptyMessage é opcional. Indica a mensagem a apresentar quando a lista estiver vazia. Por predefinição, é «no records found». Neste caso, será:
![]() |
- linhas 13-15: geram o cabeçalho [1],
- linhas 16-21: geram a coluna [2],
- linhas 22-27: geram a coluna [3],
- linhas 28-33: geram a coluna [4],
- linhas 34-41: geram a coluna [5].
O link [Retirer] permite remover uma pessoa da lista. Na linha [38], é o método [Form].retirerPersonne que realiza esta tarefa. É necessário indicar o número da pessoa a remover. Este é fornecido na linha 39. Na linha 38, utilizou-se o atributo «action». Noutras ocasiões, utilizou-se o atributo actionListener. Não tenho a certeza se compreendo bem a diferença funcional entre estes dois atributos. Na prática, verifica-se, no entanto, que os atributos definidos pelas balizas <setPropertyActionListener> são definidos antes da execução do método designado pelo atributo «action», o que não acontece com o atributo actionListener. Em suma, sempre que houver parâmetros a enviar para a ação chamada, deve utilizar-se o atributo action.
O método para remover uma pessoa é o seguinte:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable{
// modelo
private List<Personne> personnes;
private int personneId;
public void retirerPersonne() {
// à procura da pessoa selecionada
int i = 0;
for (Personne personne : personnes) {
// pessoa atual = pessoa selecionada?
if (personne.getId() == personneId) {
// remover a pessoa atual da lista
personnes.remove(i);
// concluído
break;
} else {
// pessoa seguinte
i++;
}
}
}
...
}
5.14. Exemplo: mv-pf-11: dataTable - 2
Este projeto apresenta uma tabela que exibe uma lista de dados, na qual é possível selecionar uma linha:
![]() |
Ao selecionar uma linha da tabela, no momento do POST, são enviadas informações ao modelo sobre a linha selecionada. Assim, já não é necessário um link [Retirer] por pessoa. Basta um único para toda a tabela.
O projeto NetBeans é idêntico ao anterior, com algumas pequenas diferenças: o formulário e o respetivo 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 um modo de seleção single ou multiple. Aqui, optámos por selecionar apenas uma linha,
- linha 13: o atributo rowkey designa um atributo dos elementos apresentados que permite selecioná-los 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 da pessoa selecionada. Graças ao atributo `rowkey` anterior, do lado do servidor será possível calcular uma referência da pessoa selecionada. Não dispomos dos detalhes do método utilizado. Podemos imaginar que a coleção é percorrida sequencialmente à procura do elemento correspondente ao rowkey selecionado. Isto significa que, se o método que associa o rowkey ao selection for mais complexo, então esse método não é utilizável,
Dito isto, o método [Form].retirerPersonne evolui da seguinte forma:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable {
// modelo
private List<Personne> personnes;
private Personne personneChoisie;
// fabricante
public Form() {
...
}
public void retirerPersonne() {
// remover a pessoa selecionada
personnes.remove(personneChoisie);
}
// getters e setters
...
}
- linha 9: em cada POST, a referência da linha 9 é inicializada com a referência, na lista da linha 8, da pessoa selecionada,
- em 18: a eliminação da pessoa fica assim simplificada. A pesquisa que realizámos no exemplo anterior foi feita através da baliza <dataTable>.
5.15. Exemplo: mv-pf-12: dataTable - 3
Este projeto é semelhante ao anterior. A visualização é, nomeadamente, idêntica:

O projeto NetBeans é idêntico ao anterior, com algumas pequenas diferenças que iremos analisar. O formulário [index.xhtml] apresenta 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>
- Na linha 6, o atributo rowkey desapareceu, mas o atributo selection mantém-se. A ligação entre os atributos rowkey e selection é agora feita através de uma classe. O atributo value da linha 5 tem agora como valor uma instância da interface Primefaces SelectableDataModel<T>. O método [Form].getPersonnes do modelo é alterado 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> {
// construtores
public DataTableModel() {
}
public DataTableModel(List<Personne> personnes) {
super(personnes);
}
@Override
public Object getRowKey(Personne personne) {
return personne.getId();
}
@Override
public Personne getRowData(String rowKey) {
// lista de pessoas
List<Personne> personnes = (List<Personne>) getWrappedData();
// a chave é um número inteiro
int key = Integer.parseInt(rowKey);
// procura-se a pessoa selecionada
for (Personne personne : personnes) {
if (personne.getId() == key) {
return personne;
}
}
// não foi encontrado nada
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 um array como parâmetro. Aqui, o nosso bean estende a classe ListDataModel,
- linhas 13-15: o construtor aceita como parâmetro a lista de pessoas que gerimos. Este parâmetro é passado para a classe pai,
- linha 18: o método getRowKey desempenha o papel do atributo rowkey que foi removido. Deve devolver o objeto que permite identificar uma pessoa de forma única, neste caso o id da pessoa,
- linha 23: o método getRowData deve devolver o objeto selecionado, a partir do seu rowkey. Portanto, neste caso, deve devolver uma pessoa a partir do seu ID. A referência assim obtida será atribuída ao objeto de destino do atributo «selection» na baliza dataTable, neste caso o atributo «selection="#{form.personneChoisie}"». O parâmetro do método é o «rowkey» do objeto selecionado pelo utilizador, sob a forma de uma cadeia de caracteres,
- linhas 24-35: fornecem 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() {
// a pessoa escolhida é removida
personnes.remove(personneChoisie);
}
Esta é a técnica a utilizar quando a ligação entre os atributos rowkey e selection não é uma simples ligação de propriedade (rowkey**) a objeto (selection**).
5.16. Exemplo: mv-pf-13: dataTable - 4
Este projeto é semelhante ao anterior, com a diferença de que o modo de seleção da pessoa a retirar muda:

Acima, vemos que o objeto é selecionado através de um menu contextual (clique com o botão direito do rato). É solicitada uma confirmação da eliminação:
![]() |
O formulário [index.xhtml] evolui da seguinte forma:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<!-- título -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- menu contextual -->
<p:contextMenu for="personnes">
<p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
</p:contextMenu>
<!-- caixa de diálogo -->
<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 contextual para (atributo «for») o dataTable da linha 21 (atributo «id»). Assim, este menu contextual surge ao clicar 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 do atributo onclick é executado. O código JavaScript [confirmation.show()] faz com que seja apresentada a caixa de diálogo da linha 14 (atributo widgetVar). Esta é a seguinte:
![]() |
- linha 14: o atributo «message» exibe [3], o atributo «header» exibe [1], o atributo «severity» exibe o ícone [2],
- linha 16: apresenta [4]. Ao clicar, a pessoa é eliminada (atributo «action»), e em seguida 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]. Ao clicar, a caixa de diálogo é fechada e a pessoa não é eliminada.
5.17. Exemplo: mv-pf-14: dataTable - 5
Este projeto demonstra que é possível obter uma resposta do servidor após a execução de uma chamada AJAX. Para tal, utiliza-se o atributo oncomplete da chamada AJAX:
![]() |
O formulário [index.xhtml] evolui da seguinte forma:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
...
<!-- caixa de diálogo 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) {
// erro?
if(args.msgErreur) {
alert(args.msgErreur);
}
}
</script>
...
</p:dataTable>
</ui:define>
</ui:composition>
</html>
- linha 7: o atributo «oncomplete» chama a função JavaScript das 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 enriquecer,
- linha 15: verifica-se se o dicionário args possui um atributo denominado «msgErreur». Se sim, este é apresentado (linha 16).
No modelo, o método [retirerPersonne] evolui da seguinte forma:
public void retirerPersonne() {
// eliminação aleatória
int i = (int) (Math.random() * 2);
if (i == 0) {
// remoção da pessoa selecionada
personnes.remove(personneChoisie);
} else {
// é devolvido um erro
String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
}
}
- linha 3: gera-se 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 tem como objetivo adicionar ao dicionário args, de que já falámos, o atributo denominado «msgErreur» com o valor msgErreur gerado na linha 9. Este atributo é posteriormente recuperado pelo método JavaScript de [index.xhtml]:
<!-- JavaScript -->
<script type="text/javascript">
function handleRequest(xhr, status, args) {
// erro?
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 destacado acima. É obtida com o código XHTML, a seguir ao [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">
<!-- título -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- barra de ferramentas-->
<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,
- linhas 19-21: o mesmo se aplica aos 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>
Exibem a imagem abaixo:
![]() |
- linha 2: apresenta [1],
- linha 3: apresenta 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: apresenta um botão [5] que serve para eliminar a pessoa selecionada na lista suspensa. O botão tem um ícone. Estes ícones são os de JQuery e UI. A lista destes ícones encontra-se em URL, [http://jqueryui.com/themeroller/] e [6]:
![]() |
- Para saber o nome de um ícone, basta passar o rato por cima dele. Em seguida, esse nome é utilizado no atributo icon do componente <commandButton>, por exemplo, icon="ui-icon-trash". Note-se que, no exemplo acima, o nome atribuído será .ui-icon-trash e que se remove o ponto inicial desse nome no atributo icon,
- linha 9: cria uma bolha de ajuda para o botão (atributo «for»). Quando se mantém o cursor sobre o botão, é exibida a mensagem de ajuda [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 {
// modelo
private List<Personne> personnes;
private int personneId;
// fabricante
public Form() {
// inicialização da lista de pessoas
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() {
// procura-se a pessoa selecionada
int i = 0;
for (Personne personne : personnes) {
// pessoa atual = pessoa selecionada?
if (personne.getId() == personneId) {
// remoção da pessoa atual da lista
personnes.remove(i);
// concluído
break;
} else {
// pessoa seguinte
i++;
}
}
}
// getters e setters
...
}
Os componentes à direita 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>
Estes apresentam 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 o mudar para inglês.
5.19. Conclusion
Já sabemos o suficiente para portar a nossa aplicação de exemplo para o Primefaces. Apenas vimos cerca de quinze componentes, quando a biblioteca possui mais de 100. O leitor é convidado a procurar o componente que lhe 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 o Tomcat em [3]. São então apresentados no navegador interno do Eclipse [3].
![]() |



















































