5. Introduzione alla libreria dei componenti PrimeFaces
5.1. Il ruolo di PrimeFaces in un’applicazione JSF
Torniamo all’architettura di un’applicazione JSF così come l’abbiamo esaminata all’inizio di questo documento:
![]() |
Le pagine JSF erano realizzate con tre librerie di tag:
- riga 2: i tag <h:x> dello spazio dei nomi [http://java.sun.com/jsf/html] che corrispondono ai tag HTML,
- riga 3: i tag <f:y> dello spazio dei nomi [http://java.sun.com/jsf/core] che corrispondono ai tag JSF,
- riga 4: i tag <ui:z> dello spazio dei nomi [http://java.sun.com/jsf/facelets] che corrispondono ai tag dei facelet.
Per creare le pagine JSF, aggiungeremo una quarta libreria di tag, quella dei componenti PrimeFaces.
- riga 3: i tag <p:z> dello spazio dei nomi [http://primefaces.org/ui] corrispondono ai componenti Primefaces.
Questa è l’unica modifica che verrà applicata. Appare quindi nelle viste. I gestori di eventi e i modelli rimangono gli stessi di JSF. È un punto importante da comprendere.
L’utilizzo dei componenti Primefaces consente di creare interfacce web più intuitive grazie ai numerosi componenti di questa libreria e più fluide grazie alla tecnologia AJAX che utilizza in modo nativo. Si parla quindi di interfacce ricche o RIA (Rich Internet Application).
La precedente architettura JSF diventerà la seguente architettura PF (PrimeFaces):
![]() |
5.2. I vantaggi di Primefaces
Il sito di Primefaces [http://www.primefaces.org/showcase/ui/home.jsf] fornisce l’elenco dei componenti utilizzabili in una pagina PF:
![]() |
Negli esempi che seguiranno, utilizzeremo le prime due funzionalità di Primefaces:
- alcuni tra i circa cento componenti disponibili,
- il comportamento nativo di questi ultimi.
Tra i componenti disponibili:
![]() | ![]() | ![]() |
Nei nostri esempi ne useremo solo una quindicina, ma saranno sufficienti per comprendere i principi di costruzione di una pagina Primefaces.
5.3. Imparare a usare Primefaces
Primefaces offre esempi di utilizzo di ciascuno dei suoi componenti. Basta cliccare sul relativo link. Vediamo un esempio:
![]() |
- in [1], l'esempio per il componente [Spinner],
- in [2], la finestra di dialogo visualizzata dopo aver cliccato sul pulsante [Submit].
Ci sono tre novità:
- il componente [Spinner], che non esiste di default in JSF,
- lo stesso vale per la finestra di dialogo,
- infine, il POST generato dal [Submit] è implementato con AJAX. Se si osserva attentamente il browser durante l’esecuzione di POST, non si vede la clessidra. La pagina non viene ricaricata. Viene semplicemente modificata: un nuovo componente, in questo caso la finestra di dialogo, appare nella pagina.
Vediamo come avviene tutto questo. Il codice XHTML dell’esempio è il seguente:
<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>
Innanzitutto, notiamo la presenza dei classici tag JSF: <h:form> alla riga 1, <h:panelGrid> alla riga 3, <h:outputLabel> alla riga 4. Alcuni tag JSF vengono ripresi da PF e arricchiti: <p:commandButton> riga 21. Successivamente, si trovano i tag PF di formattazione: <p:panel> riga 2, <p:outputPanel> riga 13, <p:dialog> riga 23. Infine, ci sono i tag di immissione dati: <p:spinner> riga 5.
Analizziamo questo codice in relazione alla vista:
![]() |
- in [1], il componente ottenuto con il tag <p:panel> della riga 2,
- in [2], il campo di immissione ottenuto dalla combinazione dei tag <p:outputLabel> e <p:spinner>, righe 6 e 7,
- in [3], il pulsante di POST ottenuto con il tag <p:commandButton> della riga 21,
- in [4], la finestra di dialogo delle righe 23-25,
- in [5], un contenitore invisibile per due componenti. Viene creato dal tag <p:outputPanel> della riga 13.
Analizziamo il codice seguente che implementa un'azione 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>
Questo codice genera la seguente visualizzazione:
![]() |
- riga 1: visualizza il testo [1]. È allo stesso tempo un'etichetta per il componente id=ajaxspinner (attributo for). Questo componente è quello della riga 3 (attributo id),
- righe 3-5: visualizzano il componente [2]. Questo componente è un componente di immissione/visualizzazione associato al modello #{spinnerController.number5} (attributo "value"),
- riga 6: visualizza il componente [3]. Questo componente è un componente di visualizzazione collegato al modello #{spinnerController.number5} (attributo value),
- riga 4: il tag <p:ajax> aggiunge un comportamento AJAX al spinner. Ogni volta che quest’ultimo cambia valore, viene eseguito un POST con tale valore (attributo process="@this") sul modello #{spinnerController.number5}. Una volta fatto ciò, viene eseguito un aggiornamento della pagina (attributo update). Questo attributo ha come valore l’ID di un componente della pagina, in questo caso quello della riga 6. Il componente di destinazione dell’attributo update viene quindi aggiornato con il modello. Quest’ultimo è nuovamente #{spinnerController.number5}, ovvero il valore di spinner. In questo modo, il campo [3] segue gli inserimenti effettuati nel campo [2].
Si tratta di un comportamento AJAX, acronimo che sta per Asynchronous Javascript And XML. In generale, un comportamento AJAX è il seguente:
![]() |
- il browser visualizza una pagina HTML contenente codice JavaScript (J di AJAX). Gli elementi della pagina formano un oggetto JavaScript denominato DOM (Document Object Model),
- il server ospita l’applicazione web che ha generato questa pagina,
- in [1], si verifica un evento nella pagina. Ad esempio, l’incremento di spinner. Questo evento viene gestito da JavaScript,
- In [2], il JavaScript esegue un’operazione POST sull’applicazione web. Lo fa in modo asincrono (la A di AJAX). L’utente può continuare a lavorare con la pagina. Non è bloccata, ma all’occorrenza è possibile bloccarla. Il POST aggiorna il modello della pagina in base ai valori inviati via POST, in questo caso il modello #{spinnerController.number5},
- in [3]; l’applicazione web restituisce a JavaScript una risposta XML (la X di AJAX) o JSON (JavaScript in notazione oggetto),
- in [4], il JavaScript utilizza questa risposta per aggiornare un'area specifica del DOM, in questo caso l'area di id=ajaxspinnervalue.
Quando si utilizzano JSF e Primefaces, il codice JavaScript viene generato da Primefaces. Questa libreria si basa sulla libreria JavaScript JQuery. Allo stesso modo, i componenti Primefaces si basano su quelli della libreria di componenti JQuery e UI (User Interface). Pertanto, JQuery costituisce la base di Primefaces.
Torniamo al nostro esempio e presentiamo ora il componente POST del pulsante [Submit]:
![]() |
Il codice associato a POST è il seguente:
<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>
- riga 1: il POST è generato dal pulsante della riga 1. In Primefaces, i tag che generano un POST lo fanno per impostazione predefinita sotto forma di una chiamata AJAX. Ecco perché questi tag dispongono di un attributo `update` per indicare l’area da aggiornare una volta ricevuta la risposta dal server. In questo caso, l’area aggiornata è il panelGrid della riga 4. Pertanto, al ritorno del POST, quest’area verrà aggiornata con i valori inviati al modello. Tuttavia, si trovano all’interno di una finestra di dialogo non visibile per impostazione predefinita. È l’attributo `oncomplete` della riga 1 che la visualizza. Questo evento si verifica al termine dell’elaborazione del POST. Il valore di questo attributo è codice JavaScript. Qui viene visualizzata la finestra di dialogo con id=dialog, ovvero quella della riga 3 (attributo widgetVar),
- riga 3: si vedono vari attributi della finestra di dialogo. Bisogna sperimentare per capire a cosa servono.
Abbiamo parlato del modello ma non lo abbiamo ancora presentato. Ecco qui:
In generale, si può procedere come segue:
- individuare il componente Primefaces che si desidera utilizzare,
- esaminarne l’esempio. Gli esempi di Primefaces sono ben fatti e facilmente comprensibili.
5.4. Un primo progetto Primefaces: mv-pf-01
Creiamo un progetto web Maven con NetBeans:
![]() |
- [1, 2, 3]: creiamo un progetto Maven di tipo [Web Application],
![]() |
- [4]: il server sarà Tomcat,
- in [5], il progetto generato,
- in [6], lo si ripulisce dal file [index.jsp] e dal pacchetto Java,
![]() |
- in [7, 8]: nelle proprietà del progetto si aggiunge il supporto per Java Server Faces,
![]() |
- in [9]; nella scheda [Components] si seleziona la libreria di componenti PrimeFaces. NetBeans offre il supporto per altre librerie di componenti: ICEFaces e RichFaces.
- In [10], il progetto generato. In [11], si noti la dipendenza da Primefaces.
In sostanza, un progetto Primefaces è un classico progetto JSF a cui è stata aggiunta una dipendenza da Primefaces. Nient’altro.
Una volta compreso questo, modifichiamo il file [pom.xml] per lavorare con le ultime versioni delle librerie:
<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>
Alle righe 26-30, si noti il repository Maven per Primefaces. Una volta apportate queste modifiche, si compila il progetto per avviare il download delle dipendenze. Si ottiene così il progetto [12].
Ora proviamo a riprodurre l’esempio che abbiamo studiato. La pagina [index.html] diventa la seguente:
<?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>
<!-- modulo -->
<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()" />
<!-- finestra di dialogo -->
<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>
Non dimentichiamo la riga 5, che dichiara lo spazio dei nomi della libreria dei tag Primefaces. Aggiungiamo al progetto il bean che funge da modello per la pagina:
![]() |
Il bean è il seguente:
package beans;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class SpinnerController {
// modello
private int number1;
private double number2;
private int number3;
private int number4;
private int number5;
// getters e setters
...
}
La classe è un bean (riga 6) con ambito di richiesta (riga 7). Poiché non è stato specificato alcun nome, il bean prende il nome dalla classe con la prima lettera minuscola: spinnerController.
Quando si esegue il progetto, si ottiene quanto segue:
![]() |
Abbiamo così mostrato come testare un esempio tratto dal sito di Primefaces. Tutti gli esempi possono essere testati in questo modo.
Di seguito ci concentreremo solo su alcuni componenti di Primefaces. Per prima cosa riprenderemo gli esempi studiati con JSF e sostituiremo alcuni tag JSF con tag Primefaces. L’aspetto delle pagine subirà alcune modifiche: avranno un comportamento AJAX, ma i bean associati non dovranno essere modificati. In ciascuno degli esempi che seguiranno, ci limiteremo a presentare il codice delle pagine e le relative schermate. Il lettore è invitato a testare gli esempi per individuare le differenze tra le pagine e quelle.
5.5. Esempio mv-pf-02: gestore di eventi – internazionalizzazione – navigazione tra le pagine
Questo progetto è il porting del progetto JSF [mv-jsf2-02] (paragrafo 2.4, pagina 41):
![]() | ![]() |
Il progetto NetBeans è il seguente:
![]() |
La pagina [index.html] è la seguente:
<?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>
Alle righe 15, 16 e 19 i tag <h:commandLink> sono stati sostituiti dai tag <p:commandLink>. Questo tag ha un comportamento predefinito AJAX che può essere disabilitato impostando l'attributo ajax="false". Pertanto, in questo caso, i tag <p:commandLink> si comportano come i tag <h:commandLink>: cliccando su questi link si verificherà un ricaricamento della pagina.
5.6. Esempio mv-pf-03: impaginazione tramite facelet
Questo progetto illustra la creazione di pagine XHTML utilizzando i modelli facelet dell’esempio [mv-jsf2-09] (paragrafo 2.11):
![]() |
Il progetto NetBeans è il seguente:
![]() |
- in [1], i file di configurazione del progetto JSF,
- in [2], le pagine XHTML,
- in [3], il bean di supporto per il cambio di lingua,
- in [4], i file dei messaggi,
- in [5], le dipendenze.
Le pagine del progetto hanno come modello la pagina [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>
- riga 9: un tag <f:view> racchiude l'intera pagina per sfruttare l'internazionalizzazione che esso consente,
- riga 15: un modulo con l'ID «form». Questo modulo costituisce il corpo della pagina. In questo corpo, c'è solo una parte dinamica, quella delle righe 28-30. È lì che verrà inserita la parte variabile della pagina:
![]() |
- l'area incorniciata sopra verrà aggiornata tramite chiamate a AJAX. Per identificarla, l'abbiamo inclusa in un contenitore Primefaces generato dal tag <p:outputPanel> (riga 27). E questo contenitore è stato denominato «contenuto» (attributo id). Poiché si trova in un modulo che è a sua volta un contenitore denominato «modulo», il nome completo dell’area dinamica è:modulo:contenuto. Il primo «:» indica che si parte dalla radice del documento, poi si passa nel contenitore denominato «modulo», quindi nel contenitore denominato «contenuto». Una difficoltà con AJAX consiste nel denominare correttamente le aree da aggiornare tramite una chiamata a AJAX. Il modo più semplice è esaminare il codice sorgente della pagina HTML ricevuta:
Come si vede sopra, il tag <h:outputPanel> ha generato un tag HTML <span>. In questo esempio, il nome relativo «form:contenuto» (senza il «:» iniziale) e il nome completo «:form:contenuto» (con il «:» iniziale) indicano lo stesso oggetto.
Si noti che le chiamate AJAX (<p:commandButton>, <p:commandLink>) che aggiornano l’area dinamica avranno l’attributo update=":formulaire:contenu".
La pagina [index.xhtml] è l’unica pagina visualizzata dal progetto:
<?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>
- alla riga 8, il modello di [index.xhtml] è la pagina [layout.xhtml] appena presentata,
- riga 9: è l’area dell’ID contenuto che viene aggiornata da [index.html]. In quest’area sono presenti due frammenti:
- il frammento [page1.xhtml] alla riga 11;
- il frammento [page2.xhtml] alla riga 14.
Questi due frammenti si escludono a vicenda.
- riga 10: il frammento [page1.xhtml] viene visualizzato se la richiesta ha l’attributo page1 impostato su true o se l’attributo page2 non esiste. È il caso della primissima richiesta, in cui nessuno di questi attributi sarà presente nella richiesta. In questo caso, verrà visualizzato il frammento [page1.xhtml],
- riga 11; il frammento [page2.xhtml] viene visualizzato se la richiesta ha l'attributo page2 impostato su true
Il frammento [page1.xhtml] è il seguente:
<?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 visualizza il seguente contenuto:
![]() |
- righe 11 e 12, i due link per cambiare la lingua. Questi due link generano chiamate AJAX (ajax=true). Si tratta dell'impostazione predefinita. È quindi possibile omettere l'attributo ajax=true. Non lo faremo più in seguito. Si noti che questi due link aggiornano l'area :formulario:contenuto (attributo update), quella evidenziata sopra,
- riga 15: un link di navigazione AJAX che, anche in questo caso, aggiorna l’area :formulaire:contenu,
- riga 16: si utilizza il tag <h:setPropertyActionListener> per inserire l’attributo page2 nella richiesta con il valore true. Ciò avrà l’effetto di visualizzare il frammento [page2.xhtml] (riga 6 qui sotto) nella pagina [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>
Il frammento [page2.xhtml] è analogo:
![]() |
Il codice di [page2.xhtml] è il seguente:
<?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>
Da questo esempio trarremo i seguenti spunti per il seguito:
- utilizzeremo il modello [layout.xhtml] come modello per le pagine,
- l'area dinamica sarà identificata dall'ID:formulario:contenuto e verrà aggiornata tramite chiamate a AJAX.
5.7. Esempio mv-pf-04: modulo di inserimento dati
Questo progetto è il porting del progetto JSF2 [mv-jsf2-03] (cfr. paragrafo 2.5):
![]() |
Il progetto NetBeans è il seguente:
![]() |
Sopra, in [1], le pagine XHTML del progetto. L'impaginazione è garantita dal modello [layout.xhtml] esaminato in precedenza. La pagina [index.xhtml] è l'unica pagina del progetto. Viene visualizzata nell'area :modulo:contenuto. Il suo codice è il seguente:
<?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>
Si limita a visualizzare il frammento [page1.xhtml]. Quest’ultimo è l’equivalente del modulo esaminato nell’esempio [mv-jsf2-03]. Ricordiamo che lo scopo di quest’ultimo era quello di presentare i tag di immissione dati JSF. Tali tag sono stati qui sostituiti da tag Primefaces.
PanelGrid
Per formattare gli elementi di [page1.xhtml], utilizziamo il tag <p:panelGrid>. Ad esempio, per i due link delle lingue:
<!-- lingue -->
<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>
Il risultato è il seguente:
Un’altra variante del tag <p:panelGrid> è la seguente:
<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>
Le righe e le colonne della tabella sono identificate dai tag <p:row> e <p:column>.
Le righe da 3 a 12 definiscono l'intestazione della tabella:
Le righe da 14 a 25 definiscono una riga della tabella:
Le righe 27-35 definiscono il piè di pagina della tabella:
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>
![]() |
Alla riga 7, l'attributo feedback=true consente di ottenere un feedback sulla qualità [1] della password.
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>
![]() |
Riga 7, il tag <p:editor> visualizza un editor avanzato che consente di formattare il testo (carattere, dimensione, colore, allineamento, ...). Ciò che viene inviato al server è il codice HTML del testo digitato [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>
![]() |
Alla riga 14, si noti che il collegamento [Raz] aggiorna AJAX dell'area :formulario:selectManyMenu, che corrisponde al componente della riga 6. Tuttavia, è importante sapere che durante l’aggiornamento POST AJAX, vengono inviati tutti i valori del modulo. Pertanto, viene aggiornato l’intero modello. Con questo modello, tuttavia, viene aggiornato solo il campo :formulaire: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. Esempio: mv-pf-05: elenchi dinamici
Questo progetto è il porting del progetto JSF2 [mv-jsf2-04] (cfr. paragrafo 2.6):

Questo progetto non introduce nuovi tag PrimeFaces rispetto al progetto precedente. Pertanto non lo commenteremo. Fa parte dell'elenco di esempi messi a disposizione del lettore sul sito del documento.
5.9. Esempio: mv-pf-06: navigazione – sessione – gestione delle eccezioni
Questo progetto è il porting del progetto JSF2 [mv-jsf2-05] (cfr. paragrafo 2.7):
![]() |
Anche in questo caso, l’esempio non introduce nuovi tag PrimeFaces. Ci limiteremo a commentare la tabella dei link riportata nel riquadro sopra:
<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>
- tutti i link hanno l’attributo ajax=false. Il caricamento della pagina avviene quindi normalmente,
- si notino le righe 2 e 4, che illustrano come effettuare un reindirizzamento.
5.10. Esempio: mv-pf-07: convalida e conversione dei dati inseriti
Questo progetto è il porting del progetto JSF2 [mv-jsf2-06] (cfr. paragrafo 2.8):

L’applicazione introduce due nuovi tag, il tag <p:messages>:
<p:messages globalOnly="true"/>

e il tag <p:message>:
<p:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<p:message for="saisie1" styleClass="error"/>
Rispetto al tag <h:message> di JSF, il tag <p:message> di PF comporta le seguenti modifiche:
- l'aspetto del messaggio di errore è diverso in [1],
- il campo di immissione errato è circondato da un riquadro rosso in [2].
5.11. Esempio: mv-pf-08: eventi legati al cambiamento di stato dei componenti
Questo progetto è il porting del progetto JSF2 [mv-jsf2-07] (cfr. paragrafo 2.9):

Il progetto JSF introduceva il concetto di listeners. La gestione di listener con Primefaces è stata effettuata in modo diverso.
Con JSF:
Con Primefaces:
- riga 2: il tag <h:selectOneMenu> senza l'attributo valueChangeListener,
- riga 4: il tag <p:ajax> aggiunge un comportamento AJAX al suo tag padre <h:selectOneMenu>. Per impostazione predefinita, reagisce all'evento "cambio di valore" dell'elenco combo1. A seguito di questo evento, i valori del modulo a cui appartiene verranno inviati al server tramite una chiamata AJAX. Il modello viene quindi aggiornato. Si utilizza questo nuovo modello per aggiornare l’elenco a discesa identificato da combo2 (riga 10). Si noti, alla riga 4, che la chiamata AJAX non esegue alcun metodo del modello. In questo caso non è necessario. Si desidera semplicemente modificare il modello tramite POST in base ai valori inseriti.
5.12. Esempio: mv-pf-09: inserimento assistito
Questo progetto presenta tag di immissione specifici di Primefaces che facilitano l’immissione di determinati tipi di dati:
![]() |
5.12.1. Il progetto NetBeans
Il progetto NetBeans è il seguente:
![]() |
L'interesse del progetto risiede nella:
- l'unica pagina [index.html] visualizzata da quest'ultimo,
- il modello [Form.java] di quest'ultima.
5.12.2. Il modello
Il modulo presenta quattro campi di immissione associati al seguente modello:
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) {
...
}
// getter e setter
...
}
I quattro dati inseriti sono associati ai campi delle righe 14-17.
5.12.3. Il modulo
Il modulo è il seguente:
<?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"/>
<!-- calendario -->
...
<!-- slider -->
...
<!-- spinner -->
...
<!-- autocomplete -->
...
</p:panelGrid>
</ui:define>
</ui:composition>
</html>
Esaminiamo i quattro campi di inserimento.
5.12.4. Il calendario
Il tag <p:calendar> consente di selezionare una data da un calendario. Questo tag ammette diversi attributi.
<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>
Alla riga 2 si specifica che la data deve essere visualizzata nel formato "gg/mm/aaaa" e che il fuso orario è quello di Parigi. Quando si posiziona il cursore nell'area di immissione, viene visualizzato un calendario:
![]() |
5.12.5. Il cursore
Il tag <p:slider> consente di inserire un numero intero trascinando un cursore lungo una barra:
Il codice del tag è il seguente:
<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}"/>
- riga 3: si tratta di un classico tag <p:inputText> che consente di inserire il numero intero. Quest'ultimo può essere inserito anche tramite lo slider,
- riga 4: il tag <p:slider> è associato al tag di immissione <p:inputText> (attributo for). Gli si assegnano un valore minimo e un valore massimo.
5.12.6. Lo spinner
Abbiamo già avuto modo di presentare questo 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}"/>
Riga 3: lo spinner consente di inserire un numero intero compreso tra 1 e 12. È possibile inserire il numero direttamente nell’area di immissione dello spinner oppure utilizzare le frecce per aumentare o diminuire il valore inserito.
5.12.7. Il completamento automatico
L'inserimento assistito consiste nel digitare i primi caratteri del testo. A quel punto compaiono dei suggerimenti in un elenco a discesa. È possibile selezionarne uno. Si utilizza questo componente al posto degli elenchi a discesa quando questi ultimi hanno un contenuto troppo ampio. Supponiamo di voler proporre un elenco a discesa delle città della Francia. Si tratta di diverse migliaia di città. Se si lascia che l’utente digiti i primi tre caratteri del nome della città, è possibile proporgli un elenco ridotto delle città che iniziano con tali caratteri.
![]() |
Il codice di questo componente è il seguente:
<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/>
Il tag <p:autoComplete> alla riga 2 è quello che consente l'inserimento assistito. Il parametro che ci interessa in questo caso è l’attributo completeMethod, il cui valore è il nome di un metodo del modello, responsabile di fornire suggerimenti corrispondenti ai caratteri digitati dall’utente. Questo metodo è il seguente:
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;
}
- riga 1: il metodo riceve come parametro la stringa dei caratteri digitati dall’utente nell’area di immissione. Restituisce un elenco di suggerimenti,
- righe 4-6: si costruisce un elenco di 10 proposte che riprende i caratteri ricevuti come parametri e vi aggiunge una cifra compresa tra 0 e 9.
5.12.8. Il tag <p:growl>
Il tag <p:growl> è un'alternativa possibile al tag <p:messages> che visualizza i messaggi di errore del modulo.
<p:growl id="messages" autoUpdate="true"/>
Nell'esempio sopra riportato, l'attributo id non viene utilizzato. L'attributo autoUpdate=true indica che l'elenco dei messaggi di errore deve essere aggiornato ad ogni POST del modulo.
Supponiamo di inviare il seguente modulo [1]:
![]() |
- in [2], il tag <p:growl> visualizza quindi i messaggi di errore associati ai dati inseriti in modo errato.
5.13. Esempio: mv-pf-10: dataTable - 1
Questo progetto presenta il tag <p:dataTable>, utilizzato per visualizzare elenchi di dati

5.13.1. Il progetto NetBeans
Il progetto NetBeans è il seguente:
![]() |
L'interesse del progetto risiede in:
- l'unica pagina [index.html] visualizzata da quest'ultimo,
- il modello [Form.java] di quest'ultima e il bean [Personne].
5.13.2. Il file dei messaggi
Il file [messages_fr.properties] è il seguente:
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. Il modello
Il bean [Personne] rappresenta una persona:
package forms;
import java.io.Serializable;
public class Personne implements Serializable{
// dati
private int id;
private String nom;
private String prénom;
// costruttori
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 e setter
...
}
Il modello della pagina [index.xhtml] è la seguente 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{
// modello
private List<Personne> personnes;
private int personneId;
// costruttore
public Form() {
// inizializzazione dell'elenco delle persone
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() {
...
}
// getter e setter
...
}
- righe 9-10: il bean ha ambito di sessione,
- righe 18-24: il costruttore crea un elenco di tre persone, che rimarrà quindi attivo per tutta la durata delle richieste,
- riga 15: il numero di una persona da eliminare dall'elenco,
- righe 26-28: il metodo di eliminazione.
5.13.4. Il modulo
Il modulo è il seguente [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>
Ciò genera la seguente visualizzazione (riquadro sottostante):
![]() |
- riga 12: genera la tabella riportata nel riquadro sopra. L'attributo value indica la collezione visualizzata dalla tabella, in questo caso l'elenco delle persone del modello. L'attributo emptyMessage è facoltativo. Indica il messaggio da visualizzare quando l'elenco è vuoto. Per impostazione predefinita è «no records found». In questo caso sarà:
![]() |
- righe 13-15: generano l'intestazione [1],
- righe 16-21: generano la colonna [2],
- righe 22-27: generano la colonna [3],
- righe 28-33: generano la colonna [4],
- righe 34-41: generano la colonna [5].
Il collegamento [Retirer] consente di rimuovere una persona dall'elenco. Riga [38]: è il metodo [Form].retirerPersonne che svolge questa operazione. È necessario conoscere il numero della persona da rimuovere. Questo viene fornito alla riga 39. Alla riga 38 è stato utilizzato l’attributo action. In altre occasioni è stato utilizzato l’attributo actionListener. Non sono sicuro di comprendere appieno la differenza funzionale tra questi due attributi. All’uso, si nota tuttavia che gli attributi impostati dai tag <setPropertyActionListener> vengono impostati prima dell’esecuzione del metodo indicato dall’attributo action, mentre ciò non avviene per l’attributo actionListener. In altre parole, non appena si hanno dei parametri da inviare all’azione chiamata, è necessario utilizzare l’attributo action.
Il metodo per rimuovere una persona è il seguente:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable{
// modello
private List<Personne> personnes;
private int personneId;
public void retirerPersonne() {
// si sta cercando la persona selezionata
int i = 0;
for (Personne personne : personnes) {
// persona corrente = persona selezionata?
if (personne.getId() == personneId) {
// si elimina la persona corrente dall'elenco
personnes.remove(i);
// operazione completata
break;
} else {
// persona successiva
i++;
}
}
}
...
}
5.14. Esempio: mv-pf-11: dataTable - 2
Questo progetto presenta una tabella che mostra un elenco di dati in cui è possibile selezionare una riga:
![]() |
La selezione di una riga della tabella invia, al momento del POST, informazioni al modello relative alla riga selezionata. Di conseguenza, non è più necessario un collegamento [Retirer] per ogni persona. Ne basta uno solo per l’intera tabella.
Il progetto NetBeans è identico al precedente, salvo alcuni dettagli: il modulo e il relativo modello. Il modulo [index.xhtml] è il seguente:
<?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>
- riga 13: l'attributo selectionMode consente di scegliere una modalità di selezione single o multiple. In questo caso, abbiamo scelto di selezionare una sola riga,
- riga 13: l’attributo rowkey indica un attributo degli elementi visualizzati che permette di selezionarli in modo univoco. In questo caso, abbiamo scelto l’ID della persona selezionata,
- riga 13: l’attributo selection indica l’attributo del modello che riceverà un riferimento alla persona selezionata. Grazie al precedente attributo rowkey, lato server sarà possibile calcolare un riferimento alla persona selezionata. Non disponiamo dei dettagli del metodo utilizzato. Si può ipotizzare che la collezione venga percorsa in modo sequenziale alla ricerca dell’elemento corrispondente al rowkey selezionato. Ciò significa che se il metodo che associa rowkey a selection è più complesso, allora tale metodo non è utilizzabile,
Detto questo, il metodo [Form].retirerPersonne viene modificato come segue:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable {
// modello
private List<Personne> personnes;
private Personne personneChoisie;
// costruttore
public Form() {
...
}
public void retirerPersonne() {
// si rimuove la persona selezionata
personnes.remove(personneChoisie);
}
// getter e setter
...
}
- riga 9: per ogni POST, il riferimento della riga 9 viene inizializzato con il riferimento, nell’elenco della riga 8, della persona selezionata,
- in 18: la cancellazione della persona risulta così semplificata. La ricerca che avevamo effettuato nell’esempio precedente è stata effettuata tramite il tag <dataTable>.
5.15. Esempio: mv-pf-12: dataTable - 3
Questo progetto è analogo al precedente. La visualizzazione è in particolare identica:

Il progetto NetBeans è identico al precedente, salvo alcuni dettagli che esamineremo di seguito. Il modulo [index.xhtml] si presenta come segue:
...
<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>
- alla riga 6, l'attributo rowkey è stato rimosso, mentre l'attributo selection rimane. Il collegamento tra gli attributi rowkey e selection avviene ora tramite una classe. L'attributo value della riga 5 ha ora come valore un'istanza dell'interfaccia Primefaces SelectableDataModel<T>. Il metodo [Form].getPersonnes del modello viene modificato come segue:
public DataTableModel getPersonnes() {
return new DataTableModel(personnes);
}
Viene quindi aggiunto un nuovo bean al progetto:
![]() |
Questo bean è il seguente:
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> {
// costruttori
public DataTableModel() {
}
public DataTableModel(List<Personne> personnes) {
super(personnes);
}
@Override
public Object getRowKey(Personne personne) {
return personne.getId();
}
@Override
public Personne getRowData(String rowKey) {
// elenco delle persone
List<Personne> personnes = (List<Personne>) getWrappedData();
// la chiave è un numero intero
int key = Integer.parseInt(rowKey);
// si cerca la persona selezionata
for (Personne personne : personnes) {
if (personne.getId() == key) {
return personne;
}
}
// non è stato trovato nulla
return null;
}
}
- riga 7: la classe è un'istanza dell'interfaccia SelectableDataModel. Almeno due classi implementano questa interfaccia: ListDataModel, il cui costruttore accetta un elenco come parametro, e ArrayDataModel, il cui costruttore accetta un array come parametro. In questo caso, il nostro bean estende la classe ListDataModel,
- righe 13-15: il costruttore accetta come parametro l’elenco delle persone che gestiamo. Questo parametro viene passato alla classe padre,
- riga 18: il metodo getRowKey svolge il ruolo dell’attributo rowkey che è stato rimosso. Deve restituire l’oggetto che permette di identificare una persona in modo univoco, in questo caso l’ID della persona,
- riga 23: il metodo getRowData deve restituire l’oggetto selezionato, in base al suo rowkey. Quindi, in questo caso, restituire una persona in base al suo ID. Il riferimento così ottenuto verrà assegnato all’oggetto di destinazione dell’attributo selection nel tag dataTable, in questo caso l’attributo selection="#{form.personneChoisie}". Il parametro del metodo è il rowkey dell’oggetto selezionato dall’utente, sotto forma di stringa di caratteri,
- righe 24-35: restituiscono il riferimento alla persona di cui è stato ricevuto l’ID. Questo riferimento verrà assegnato al modello [Form].personneChoisie. Il metodo [retirerPersonne] rimane quindi invariato:
public void retirerPersonne() {
// si rimuove la persona selezionata
personnes.remove(personneChoisie);
}
Questa è la tecnica da utilizzare quando il collegamento tra gli attributi rowkey e selection non è un semplice collegamento da proprietà (rowkey) a oggetto (selection).
5.16. Esempio: mv-pf-13: dataTable - 4
Questo progetto è analogo al precedente, tranne per il fatto che cambia la modalità di selezione della persona da rimuovere:

Nell’esempio sopra riportato, vediamo che l’oggetto viene selezionato tramite un menu contestuale (clic con il tasto destro del mouse). Viene richiesta una conferma dell’eliminazione:
![]() |
Il modulo [index.xhtml] si evolve come segue:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<!-- titolo -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- menu contestuale -->
<p:contextMenu for="personnes">
<p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
</p:contextMenu>
<!-- finestra di dialogo -->
<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>
- righe 9-11: definiscono un menu contestuale per (attributo for) il dataTable della riga 21 (attributo id). È quindi cliccando con il tasto destro sulla tabella delle persone che appare questo menu contestuale,
- riga 10: il nostro menu ha una sola opzione (tag menuItem). Quando si fa clic su questa opzione, viene eseguito il codice JavaScript dell’attributo onclick. Il codice JavaScript [confirmation.show()] fa apparire la finestra di dialogo della riga 14 (attributo widgetVar). Questa è la seguente:
![]() |
- riga 14: l’attributo message fa visualizzare [3], l’attributo header fa visualizzare [1], l’attributo severity fa visualizzare l’icona [2],
- riga 16: visualizza [4]. Cliccando, la persona viene eliminata (attributo action), quindi la finestra di dialogo viene chiusa (attributo oncomplete). L'attributo oncomplete è codice JavaScript che viene eseguito una volta che l'azione lato server è stata completata,
- riga 17: visualizza [5]. Cliccando, la finestra di dialogo viene chiusa e la persona non viene eliminata.
5.17. Esempio: mv-pf-14: dataTable - 5
Questo progetto dimostra che è possibile ottenere un riscontro dal server dopo l’esecuzione di una chiamata AJAX. A tal fine si utilizza l’attributo oncomplete della chiamata AJAX:
![]() |
Il modulo [index.xhtml] si evolve come segue:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
...
<!-- finestra di dialogo 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) {
// errore?
if(args.msgErreur) {
alert(args.msgErreur);
}
}
</script>
...
</p:dataTable>
</ui:define>
</ui:composition>
</html>
- riga 7: l'attributo oncomplete richiama la funzione JavaScript delle righe 13-18,
- riga 13: la firma del metodo deve essere questa. args è un dizionario che il modello lato server può arricchire,
- riga 15: si verifica se il dizionario args contiene un attributo denominato 'msgErreur'. In caso affermativo, viene visualizzato (riga 16).
Nel modello, il metodo [retirerPersonne] si presenta come segue:
public void retirerPersonne() {
// eliminazione casuale
int i = (int) (Math.random() * 2);
if (i == 0) {
// si rimuove la persona selezionata
personnes.remove(personneChoisie);
} else {
// viene restituito un errore
String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
}
}
- riga 3: viene generato un numero casuale 0 o 1,
- righe 4-6: se è 0, la persona selezionata dall'utente viene rimossa dall'elenco delle persone,
- riga 9: altrimenti, viene generato un messaggio di errore internazionalizzato:
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.
- riga 10: un'istruzione complessa che ha lo scopo di aggiungere nel dizionario args di cui abbiamo parlato l'attributo denominato 'msgErreur' con il valore msgErreur generato alla riga 9. Questo attributo viene poi recuperato dal metodo JavaScript [index.xhtml]:
<!-- JavaScript -->
<script type="text/javascript">
function handleRequest(xhr, status, args) {
// errore?
if(args.msgErreur) {
alert(args.msgErreur);
}
}
</script>
5.18. Esempio: mv-pf-15: la barra degli strumenti
In questo progetto realizziamo una barra degli strumenti:
![]() |
La barra degli strumenti è il componente evidenziato nel riquadro sopra. Si ottiene con il codice XHTML seguente a [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">
<!-- titolo -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- barra degli strumenti-->
<p:toolbar>
<p:toolbarGroup align="left">
...
</p:toolbarGroup>
<p:toolbarGroup align="right">
...
</p:toolbarGroup>
</p:toolbar>
</ui:define>
</ui:composition>
</html>
- righe 15-22: la barra degli strumenti,
- righe 16-18: definiscono il gruppo di componenti a sinistra della barra,
- righe 19-21: lo stesso vale per i componenti a destra.
I componenti a sinistra della barra degli strumenti sono i seguenti:
<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>
Visualizzano la vista riportata di seguito:
![]() |
- riga 2: visualizza [1],
- riga 3: visualizza uno spazio di 30 pixel [2],
- righe 4-6: visualizzano un menu a tendina con un elenco di persone [3],
- riga 7: visualizza un separatore [4],
- riga 8: visualizza un pulsante [5] che serve a eliminare la persona selezionata nell'elenco a discesa. Il pulsante presenta un'icona. Queste icone sono quelle di JQuery e UI. Il loro elenco si trova in URL, [http://jqueryui.com/themeroller/] e [6]:
![]() |
- per conoscere il nome di un'icona, è sufficiente passarci sopra con il mouse. Successivamente, tale nome viene utilizzato nell'attributo icon del componente <commandButton>, ad esempio icon="ui-icon-trash". Si noti che nell’esempio sopra riportato il nome assegnato è .ui-icon-trash e che il punto iniziale di tale nome viene rimosso nell’attributo icon,
- riga 9: crea un fumetto di aiuto per il pulsante (attributo for). Quando si posiziona il cursore sul pulsante, viene visualizzato il messaggio di aiuto [7].
Il modello associato a questi componenti è il seguente:
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 {
// modello
private List<Personne> personnes;
private int personneId;
// costruttore
public Form() {
// inizializzazione dell'elenco delle persone
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() {
// si cerca la persona selezionata
int i = 0;
for (Personne personne : personnes) {
// persona corrente = persona selezionata?
if (personne.getId() == personneId) {
// si elimina la persona corrente dall'elenco
personnes.remove(i);
// operazione completata
break;
} else {
// persona successiva
i++;
}
}
}
// getter e setter
...
}
I componenti a destra della barra degli strumenti sono i seguenti:
<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>
Mostrano la vista riportata di seguito:
![]() |
- righe 6-9: un pulsante di menu. Contiene le opzioni di menu,
- riga 7: l'opzione per impostare il modulo in francese,
- riga 8: l'opzione per impostarlo in inglese.
5.19. Conclusion
Ora sappiamo abbastanza per trasferire la nostra applicazione di esempio su Primefaces. Abbiamo visto solo una quindicina di componenti, mentre la libreria ne contiene più di 100. Il lettore è invitato a cercare il componente che gli manca direttamente sul sito di Primefaces.
5.20. I test con Eclipse
I progetti Maven sono disponibili sul sito degli esempi [1]:
![]() |
Una volta importati in Eclipse, è possibile eseguirli [2]. Selezionare Tomcat in [3]. Verranno quindi visualizzati nel browser interno di Eclipse [3].
![]() |



















































