Skip to content

5. Introduzione alla libreria di componenti PrimeFaces

5.1. Il ruolo di PrimeFaces in un'applicazione JSF

Torniamo all'architettura di un'applicazione JSF, così come l'abbiamo studiata all'inizio di questo documento:

Le pagine JSF sono state realizzate utilizzando tre librerie di tag:

1
2
3
4
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  • 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> nel namespace [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 facelet.

Per creare pagine JSF, aggiungeremo una quarta libreria di tag, quella dei componenti PrimeFaces.

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
      ...
xmlns:p="http://primefaces.org/ui">
  • Riga 3: I tag <p:z> dello spazio dei nomi [http://primefaces.org/ui] corrispondono ai componenti PrimeFaces.

Questa è l'unica modifica che verrà apportata. Appare quindi nelle viste. I gestori di eventi e i modelli rimangono gli stessi di JSF. Questo è un punto importante da comprendere.

L'utilizzo dei componenti PrimeFaces consente di creare interfacce web più intuitive grazie ai numerosi componenti presenti in questa libreria e più fluide grazie alla tecnologia AJAX che supporta in modo nativo. Queste sono denominate interfacce ricche o RIA (Rich Internet Applications).

La precedente architettura JSF diventerà la seguente architettura PF (PrimeFaces):

5.2. I vantaggi di PrimeFaces

Il sito web di PrimeFaces [http://www.primefaces.org/showcase/ui/home.jsf] fornisce un elenco dei componenti che possono essere utilizzati in una pagina PF:

Negli esempi che seguiranno, utilizzeremo le prime due funzionalità di PrimeFaces:

  • alcuni dei circa cento componenti offerti,
  • il loro comportamento AJAX nativo.

Tra i componenti disponibili:

Nei nostri esempi ne useremo solo una quindicina, ma saranno sufficienti per comprendere i principi alla base della creazione di una pagina PrimeFaces.

5.3. Imparare PrimeFaces

PrimeFaces fornisce esempi di utilizzo per ciascuno dei suoi componenti. Basta cliccare sul 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].

Qui ci sono tre nuove funzionalità:

  • il componente [Spinner], che non esiste di default in JSF,
  • lo stesso vale per la finestra di dialogo,
  • e infine, il POST attivato dal pulsante [Submit] viene gestito tramite AJAX. Se osservate attentamente il browser durante il POST, non vedrete la clessidra. La pagina non viene ricaricata. Viene semplicemente modificata: un nuovo componente — in questo caso, la finestra di dialogo — appare sulla pagina.

Vediamo come funziona tutto questo. Il codice XHTML per l'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 che sono presenti tag JSF standard: <h:form> alla riga 1, <h:panelGrid> alla riga 3, <h:outputLabel> alla riga 4. Alcuni tag JSF vengono riutilizzati da PF e potenziati: <p:commandButton> alla riga 21. Successivamente, troviamo i tag di formattazione PF: <p:panel> alla riga 2, <p:outputPanel> alla riga 13, <p:dialog> alla riga 23. Infine, abbiamo i tag di input: <p:spinner> alla riga 5.

Analizziamo questo codice in relazione alla vista:

  • in [1], il componente creato con il tag <p:panel> alla riga 2,
  • in [2], il campo di immissione creato combinando i tag <p:outputLabel> e <p:spinner> alle righe 6 e 7,
  • in [3], il pulsante POST creato utilizzando il tag <p:commandButton> alla riga 21,
  • in [4], la finestra di dialogo delle righe 23–25,
  • in [5], un contenitore invisibile per due componenti. È creato dal tag <p:outputPanel> alla 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]. Funge anche da etichetta per il componente con id=ajaxspinner (per l'attributo). Questo componente è quello nella riga 3 (attributo id),
  • Righe 3–5: visualizzano il componente [2]. Questo componente è un componente di input/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 il comportamento AJAX al cursore. Ogni volta che il cursore cambia valore, viene inviata una richiesta POST contenente tale valore (attributo process="@this") al modello #{spinnerController.number5}. Una volta fatto ciò, la pagina viene aggiornata (attributo update). Il valore di questo attributo è l'ID di un componente sulla pagina, in questo caso quello alla riga 6. Il componente a cui si riferisce l'attributo update viene quindi aggiornato con il modello. Si tratta ancora una volta di #{spinnerController.number5}, ovvero il valore dello spinner. Pertanto, il campo [3] segue le voci del campo [2].

Si tratta di un comportamento AJAX, acronimo che sta per Asynchronous JavaScript and XML. In generale, un comportamento AJAX funziona come segue:

  • il browser visualizza una pagina HTML contenente codice JavaScript (la "J" di AJAX). Gli elementi della pagina formano un oggetto JavaScript chiamato DOM (Document Object Model),
  • il server ospita l'applicazione web che ha generato questa pagina,
  • in [1], si verifica un evento sulla pagina. Ad esempio, l'indicatore di caricamento aumenta. Questo evento viene gestito da JavaScript,
  • in [2], JavaScript invia una richiesta POST all'applicazione web. Lo fa in modo asincrono (la A di AJAX). L'utente può continuare a lavorare con la pagina. Non viene bloccata, ma può essere bloccata se necessario. La richiesta POST aggiorna il modello della pagina in base ai valori inviati, in questo caso il modello #{spinnerController.number5},
  • in [3], l'applicazione web invia una risposta XML (la X in AJAX) o JSON (JavaScript Object Notation) a JavaScript,
  • in [4], JavaScript utilizza questa risposta per aggiornare un'area specifica del DOM, in questo caso l'area con id=ajaxspinnervalue.

Quando si utilizzano JSF e PrimeFaces, il 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 UI (User Interface). Pertanto, jQuery costituisce la base di PrimeFaces.

Torniamo al nostro esempio e ora esaminiamo la richiesta POST dal pulsante [Submit]:

Il codice associato alla richiesta 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: La richiesta POST viene attivata dal pulsante presente alla riga 1. In PrimeFaces, i tag che attivano una richiesta POST lo fanno per impostazione predefinita tramite una chiamata AJAX. Questo è il motivo per cui tali tag dispongono di un attributo `update` che consente di specificare l'area da aggiornare una volta ricevuta la risposta dal server. In questo caso, l'area da aggiornare è il panelGrid alla riga 4. Pertanto, quando viene restituita la risposta POST, quest'area verrà aggiornata con i valori inviati al modello. Tuttavia, questi valori sono contenuti in una finestra di dialogo che è nascosta per impostazione predefinita. È l'attributo oncomplete nella riga 1 che la visualizza. Questo evento si verifica al termine dell'elaborazione POST. Il valore di questo attributo è codice JavaScript. Qui, visualizziamo la finestra di dialogo con id=dialog, che è quella della riga 3 (attributo widgetVar),
  • Riga 3: Vediamo vari attributi della finestra di dialogo. Dovrai sperimentare per capire cosa fanno.

Abbiamo menzionato il modello ma non l'abbiamo ancora mostrato. Eccolo qui:

public class SpinnerController {

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

    // getters and setters
...
}

In generale, è possibile procedere come segue:

  • identificare il componente PrimeFaces che si desidera utilizzare,
  • studiare il relativo esempio. Gli esempi di PrimeFaces sono ben scritti e di facile comprensione.

5.4. Un primo progetto PrimeFaces: mv-pf-01

Creiamo un progetto web Maven con NetBeans:

  • [1, 2, 3]: creare un progetto Maven di tipo [Applicazione Web],
  • [4]: il server sarà Tomcat,
  • in [5], il progetto generato,
  • in [6], rimuovere il file [index.jsp] e il pacchetto Java,
  • in [7, 8]: nelle proprietà del progetto, aggiungiamo il supporto per Java Server Faces,
  • in [9], nella scheda [Componenti], selezioniamo la libreria di componenti PrimeFaces. NetBeans offre il supporto per altre librerie di componenti: ICEFaces e RichFaces.
  • In [10], il progetto generato. In [11], notare la dipendenza da PrimeFaces.

In breve, un progetto PrimeFaces è un progetto JSF standard a cui è stata aggiunta una dipendenza da PrimeFaces. Nient'altro.

Ora che abbiamo 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>

Righe 26–30: Notare il repository Maven per PrimeFaces. Una volta apportate queste modifiche, compilare il progetto per avviare il download delle dipendenze. Si otterrà quindi 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>
    <!-- form -->
    <h:form>
      <p:panel header="Spinners">
        <h:panelGrid id="grid" columns="2" cellpadding="5">
          <h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
          <p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
          <h:outputLabel for="spinnerStep" value="Step Factor: " />
          <p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
          <h:outputLabel for="minmax" value="Min/Max: " />
          <p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
          <h:outputLabel for="prefix" value="Prefix: " />
          <p:spinner id="prefix" prefix="$" min="0" value="#{spinnerController.number4}"/>
          <h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
          <p:outputPanel>
            <p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
              <p:ajax update="ajaxspinnervalue" process="@this" />
            </p:spinner>
            <h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
          </p:outputPanel>
        </h:panelGrid>
      </p:panel>
      <p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
      <!-- dialog box -->
      <p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
        <h:panelGrid id="display" columns="2" cellpadding="5">
          <h:outputText value="Value 1: " />
          <h:outputText value="#{spinnerController.number1}" /> 
          <h:outputText value="Value 2: " />
          <h:outputText value="#{spinnerController.number2}" /> 
          <h:outputText value="Value 3: " />
          <h:outputText value="#{spinnerController.number3}" /> 
          <h:outputText value="Value 4: " />
          <h:outputText value="#{spinnerController.number4}" /> 
          <h:outputText value="Value 5: " />
          <h:outputText value="#{spinnerController.number5}" /> 
        </h:panelGrid>
      </p:dialog>
    </h:form>
  </h:body>
</html>

Non dimenticare la riga 5, che dichiara lo spazio dei nomi per la libreria di tag PrimeFaces. Aggiungi al progetto il bean che funge da modello per la pagina:

  

Il codice è il seguente:


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

La classe è un bean (riga 6) con ambito request (riga 7). Poiché non è stato specificato alcun nome, il bean assume il nome della classe con la prima lettera minuscola: spinnerController.

Quando eseguiamo il progetto, otteniamo quanto segue:

 

Abbiamo appena mostrato come eseguire il test su un esempio tratto dal sito web di PrimeFaces. Tutti gli esempi possono essere testati in questo modo.

D'ora in poi, ci concentreremo solo su alcuni componenti PrimeFaces. Per prima cosa riprenderemo gli esempi studiati con JSF e sostituiremo alcuni tag JSF con tag PrimeFaces. L'aspetto delle pagine cambierà leggermente; avranno un comportamento AJAX, ma i bean associati non dovranno essere modificati. In ciascuno dei prossimi esempi, presenteremo semplicemente il codice XHTML delle pagine e le schermate associate. I lettori sono invitati a provare gli esempi per individuare le differenze tra le pagine JSF e quelle PF.

5.5. Esempio mv-pf-02: Gestore di eventi – Internazionalizzazione – Navigazione tra le pagine

Questo progetto è un porting del progetto JSF [mv-jsf2-02] (sezione 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 con i tag <p:commandLink>. Questo tag ha un comportamento AJAX predefinito, che può essere disabilitato impostando l'attributo ajax="false". Quindi, in questo caso, i tag <p:commandLink> si comportano come i tag <h:commandLink>: la pagina verrà ricaricata quando si fa clic su questi link.

5.6. Esempio mv-pf-03: Layout di pagina utilizzando Facelets

Questo progetto illustra la creazione di pagine XHTML utilizzando i modelli Facelets dell'esempio [mv-jsf2-09] (sezione 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 si basano sulla 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 offre,
  • riga 15: un modulo con l'ID del modulo. Questo modulo costituisce il corpo della pagina. All'interno di questo corpo, c'è una sola sezione dinamica, le righe 28–30. È qui che verrà inserita la parte variabile della pagina:
  • l'area racchiusa in un riquadro sopra verrà aggiornata tramite chiamate AJAX. Per identificarla, l'abbiamo inclusa in un contenitore PrimeFaces generato dal tag <p:outputPanel> (riga 27). E questo contenitore è stato denominato `content` (attributo id). Poiché si trova all'interno di un modulo, che è a sua volta un contenitore denominato `form`, il nome completo dell'area dinamica è `form:content`. Il primo : indica che si parte dalla radice del documento, per poi passare al contenitore denominato "form" e infine al contenitore denominato "content". Una delle difficoltà con AJAX consiste nel denominare correttamente le aree da aggiornare tramite una chiamata AJAX. Il modo più semplice è esaminare il codice sorgente della pagina HTML ricevuta:

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

Sopra, vediamo che il tag <h:outputPanel> ha generato un tag HTML <span>. In questo esempio, il nome relativo form:content (senza i due punti iniziali) e il nome completo :form:content (con i due punti iniziali) si riferiscono allo stesso oggetto.

Si noti che le chiamate AJAX (<p:commandButton>, <p:commandLink>) che aggiornano l'area dinamica avranno l'attributo update=":form:content".

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>
  • Riga 8: Il modello per [index.xhtml] è la pagina [layout.xhtml] di cui abbiamo appena parlato.
  • Riga 9: questa è l'area ID del 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. Questo è 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 attivano chiamate AJAX (ajax=true). Questo è il valore predefinito. Pertanto, non è necessario includere l'attributo ajax=true. Non lo faremo in futuro. Si noti che questi due link aggiornano l'area :form:content (attributo update), quella evidenziata sopra,
  • riga 15: un link di navigazione AJAX che aggiorna nuovamente l'area :form:content,
  • riga 16: utilizziamo il tag <h:setPropertyActionListener> per impostare l'attributo page2 nella richiesta su true. Ciò farà sì che il frammento [page2.xhtml] (riga 6 più avanti) venga visualizzato 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] è simile:

Il codice per [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, terremo a mente i seguenti punti per il futuro:

  • utilizzeremo il template [layout.xhtml] come template della pagina,
  • l'area dinamica sarà identificata dall'id:form:content e verrà aggiornata tramite chiamate AJAX.

5.7. Esempio mv-pf-04: modulo di inserimento dati

Questo progetto è un porting del progetto JSF2 [mv-jsf2-03] (vedi sezione 2.5):

Il progetto NetBeans è il seguente:

Sopra, in [1], sono riportate le pagine XHTML del progetto. Il layout è fornito dal modello [layout.xhtml] descritto in precedenza. La pagina [index.xhtml] è l'unica pagina del progetto. Viene visualizzata nell'area :form:content. 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>

Visualizza semplicemente il frammento [page1.xhtml]. Ciò equivale al modulo illustrato nell'esempio [mv-jsf2-03]. Ricordiamo che lo scopo di quell'esempio era introdurre i tag di input JSF. Questi tag sono stati sostituiti qui con i tag PrimeFaces.

PanelGrid

Per formattare gli elementi in [page1.xhtml], utilizziamo il tag <p:panelGrid>. Ad esempio, per i due link delle lingue:


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

Questo produce il seguente output:

 

Un'altra forma 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 3–12 definiscono l'intestazione della tabella:

 

Le righe 14–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>

Riga 7: L'attributo feedback=true fornisce un feedback sulla sicurezza [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 di testo avanzato che consente di formattare il testo (carattere, dimensione, colore, allineamento, ecc.). Ciò che viene inviato al server è il codice HTML del testo inserito [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>
 

Riga 14: Si noti che il link [Reset] esegue un aggiornamento AJAX sul campo :form:selectManyMenu, che corrisponde al componente alla riga 6. Tuttavia, è importante notare che durante il POST AJAX vengono inviati tutti i valori del modulo. Pertanto, viene aggiornato l'intero modello. Con questo modello, invece, viene aggiornato solo il campo :form: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 è un porting del progetto JSF2 [mv-jsf2-04] (vedi sezione 2.6):

Image

Questo progetto non introduce alcun nuovo tag PrimeFaces rispetto al progetto precedente. Pertanto, non lo commenteremo. È incluso nell'elenco degli esempi a disposizione del lettore sul sito web del documento.

5.9. Esempio: mv-pf-06: navigazione – sessione – gestione delle eccezioni

Questo progetto è un porting del progetto JSF2 [mv-jsf2-05] (vedi sezione 2.7):

Anche in questo caso, l'esempio non introduce alcun nuovo tag PrimeFaces. Ci limiteremo a commentare la tabella di link evidenziata 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. Pertanto, la pagina si carica normalmente.
  • Si notino le righe 2 e 4 per capire come eseguire un reindirizzamento.

5.10. Esempio: mv-pf-07: convalida e conversione dei dati di input

Questo progetto è un porting del progetto JSF2 [mv-jsf2-06] (vedi sezione 2.8):

Image

L'applicazione introduce due nuovi tag, il tag <p:messages>:


<p:messages globalOnly="true"/>

Image

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 introduce le seguenti modifiche:

  • l'aspetto del messaggio di errore è diverso [1],
  • il campo di immissione errato è contornato da un bordo rosso [2].

5.11. Esempio: mv-pf-08: eventi relativi ai cambiamenti di stato dei componenti

Questo progetto è un porting del progetto JSF2 [mv-jsf2-07] (vedi sezione 2.9):

Image

Il progetto JSF ha introdotto il concetto di listener. La gestione dei listener con PrimeFaces era gestita in modo diverso.

Con JSF:

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

Con PrimeFaces:

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

        <h:outputText value="#{msg['combo2.prompt']}"/>
        <p:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
          <f:selectItems value="#{form.combo2Items}"/>
        </p:selectOneMenu>
  • Riga 2: il tag <h:selectOneMenu> senza l'attributo valueChangeListener,
  • riga 4: il tag <p:ajax> aggiunge il comportamento AJAX al 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. Utilizziamo questo nuovo modello per aggiornare l'elenco a discesa identificato come combo2 (riga 10). Si noti, alla riga 4, che la chiamata AJAX non esegue alcun metodo del modello. Ciò non è necessario in questo caso. Vogliamo semplicemente aggiornare il modello inviando i valori inseriti.

5.12. Esempio: mv-pf-09: Inserimento assistito

Questo progetto presenta tag di input specifici di PrimeFaces che facilitano l'inserimento di determinati tipi di dati:

5.12.1. Il progetto NetBeans

Il progetto NetBeans è il seguente:

Il valore del progetto risiede nella:

  • la singola pagina [index.html] visualizzata dal progetto,
  • il modello [Form.java] per quella pagina.

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) {
    ...
  }
  // getters and setters
...
}

I quattro input sono associati ai campi nelle 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"/>
 
        <!-- calendar -->
              ...
 
        <!-- slider -->
              ...
 
        <!-- spinner -->
              ...
 
        <!-- autocomplete -->
              ...
 
      </p:panelGrid>
    </ui:define>
  </ui:composition>
</html>

Diamo un'occhiata ai quattro campi di immissione.

5.12.4. Il calendario

Il tag <p:calendar> consente di selezionare una data da un calendario. Questo tag supporta vari 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>

La riga 2 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 nel campo 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: questo è un tag <p:inputText> standard che consente di inserire un numero intero. Questo valore può essere inserito anche utilizzando il cursore,
  • riga 4: il tag <p:slider> è associato al tag di input <p:inputText> (per l'attributo). Impostiamo un valore minimo e massimo per esso.

5.12.6. Il selettore

Abbiamo già presentato 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: Il campo di selezione consente di inserire un numero intero compreso tra 1 e 12. È possibile digitare il numero direttamente nel campo di selezione oppure utilizzare le frecce per aumentare o diminuire il valore inserito.

 

5.12.7. Completamento automatico

Il completamento automatico consiste nel digitare i primi caratteri del testo da inserire. I suggerimenti compaiono quindi in un elenco a discesa. È possibile selezionarne uno. Questo componente viene utilizzato al posto degli elenchi a discesa quando questi ultimi contengono troppi elementi. Supponiamo di voler offrire un elenco a discesa delle città francesi. Si tratta di diverse migliaia di città. Se si permette all'utente di digitare i primi tre caratteri del nome della città, è possibile offrirgli un elenco ridotto di città che iniziano con quei caratteri.

 

Il codice per 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 è ciò che abilita il completamento automatico. Il parametro di interesse in questo caso è l'attributo completeMethod, il cui valore è il nome di un metodo nel modello responsabile della generazione di suggerimenti in base ai caratteri digitati dall'utente. Questo metodo è definito come segue:


  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 di caratteri digitata dall'utente nel campo di immissione. Restituisce un elenco di suggerimenti,
  • righe 4–6: viene costruito un elenco di 10 suggerimenti, utilizzando i caratteri ricevuti come parametri e aggiungendo a ciascuno una cifra da 0 a 9.

5.12.8. Il tag <p:growl>

Il tag <p:growl> può sostituire il tag <p:messages>, che visualizza i messaggi di errore dei moduli.


      <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 invio del modulo (POST).

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 introduce il tag <p:dataTable>, utilizzato per visualizzare elenchi di dati

Image

5.13.1. Il progetto NetBeans

Il progetto NetBeans è il seguente:

Il fascino del progetto risiede nella:

  • la singola pagina [index.html] visualizzata dal progetto,
  • il modello [Form.java] per quella pagina e il bean [Person].

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 [Person] rappresenta una persona:


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

Il modello per la 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{
 
  // model
  private List<Personne> personnes;
  private int personneId;
 
  // manufacturer
  public Form() {
    // initialization of the list of persons
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }
 
  public void retirerPersonne() {
...
  }
  
  // getters and setters
...  
}
  • righe 9-10: il bean ha ambito di sessione,
  • righe 18–24: il costruttore crea un elenco di tre persone, un elenco che persisterà tra le richieste,
  • riga 15: l'ID di una persona da rimuovere dall'elenco,
  • righe 26–28: il metodo delete.

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>

Questo produce la seguente visualizzazione (riportata nel riquadro sottostante):

  • riga 12: genera la tabella mostrata nel riquadro sopra. L'attributo value specifica la raccolta visualizzata dalla tabella, in questo caso l'elenco delle persone del modello. L'attributo emptyMessage è facoltativo. Specifica il messaggio da visualizzare quando l'elenco è vuoto. Per impostazione predefinita, è "nessun record trovato". In questo caso, sarà:
 
  • righe 13–15: genera l'intestazione [1],
  • righe 16–21: generare colonna [2],
  • righe 22–27: generare colonna [3],
  • righe 28–33: generare colonna [4],
  • righe 34-41: generare colonna [5].

Il link [Rimuovi] consente di rimuovere una persona dall'elenco. Alla riga [38], il metodo [Form].removePerson esegue questa operazione. Deve conoscere l'ID della persona da rimuovere. Questo viene fornito alla riga 39. Alla riga 38, abbiamo utilizzato l'attributo action. In altre occasioni, abbiamo utilizzato l'attributo actionListener. Non sono sicuro di comprendere appieno la differenza funzionale tra questi due attributi. In pratica, tuttavia, notiamo che gli attributi impostati dai tag <setPropertyActionListener> vengono impostati prima che venga eseguito il metodo designato dall'attributo action, mentre questo non è il caso dell'attributo actionListener. In breve, non appena ci sono 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{
 
  // model
  private List<Personne> personnes;
  private int personneId;
 
  public void retirerPersonne() {
    // search for the selected person
    int i = 0;
    for (Personne personne : personnes) {
      // current person = selected person?
      if (personne.getId() == personneId) {
        // delete the current person from the list
        personnes.remove(i);
        // we're done
        break;
      } else {
        // next person
        i++;
      }
    }
  }
...  
}

5.14. 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 nella tabella invia le informazioni relative alla riga selezionata al modello durante la richiesta POST. Di conseguenza, non è più necessario un link [Rimuovi] per ogni persona. È sufficiente un unico link per l'intera tabella.

Il progetto NetBeans è identico al precedente, con alcune piccole differenze: 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 tra modalità di selezione singola o multipla. Qui abbiamo scelto di selezionare una sola riga,
  • riga 13: l'attributo rowkey designa un attributo degli elementi visualizzati che ne consente la selezione univoca. Qui 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, è possibile calcolare un riferimento alla persona selezionata sul lato server. Non disponiamo dei dettagli del metodo utilizzato. Possiamo immaginare che la collezione venga attraversata in modo sequenziale alla ricerca dell'elemento corrispondente al rowkey selezionato. Ciò significa che se il metodo che associa rowkey alla selezione è più complesso, allora questo metodo non è utilizzabile,

Detto questo, il metodo [Form].removePerson si evolve come segue:


...
 
@ManagedBean
@SessionScoped
public class Form implements Serializable {
 
  // model
  private List<Personne> personnes;
  private Personne personneChoisie;
 
  // manufacturer
  public Form() {
  ...
  }
 
  public void retirerPersonne() {
    // we remove the chosen person
    personnes.remove(personneChoisie);
  }
 
  // getters and setters
...
}
  • riga 9: ad ogni POST, il riferimento nella riga 9 viene inizializzato con il riferimento, presente nell'elenco della riga 8, della persona selezionata,
  • in 18: questo semplifica il processo di rimozione della riga. La ricerca che abbiamo effettuato nell'esempio precedente è stata eseguita utilizzando il tag <dataTable>.

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

Questo progetto è simile al precedente. La vista è sostanzialmente identica:

Image

Il progetto NetBeans è identico al precedente, con alcune piccole differenze che esamineremo. Il modulo [index.xhtml] cambia 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>
  • Riga 6: l'attributo rowkey è stato rimosso, ma l'attributo selection rimane. Il collegamento tra gli attributi rowkey e selection è ora stabilito tramite una classe. L'attributo value alla riga 5 ora contiene un'istanza dell'interfaccia PrimeFaces SelectableDataModel<T>. Il metodo [Form].getPeople del modello viene aggiornato come segue:

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

Al progetto viene quindi aggiunto un nuovo bean:

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> {
 
  // manufacturers
  public DataTableModel() {
  }
 
  public DataTableModel(List<Personne> personnes) {
    super(personnes);
  }
 
  @Override
  public Object getRowKey(Personne personne) {
    return personne.getId();
  }
 
  @Override
  public Personne getRowData(String rowKey) {
    // list of persons
    List<Personne> personnes = (List<Personne>) getWrappedData();
    // the key is an integer 
    int key = Integer.parseInt(rowKey);
    // search for the selected person
    for (Personne personne : personnes) {
      if (personne.getId() == key) {
        return personne;
      }
    }
    // we found nothing
    return null;
  }
}
  • riga 7: la classe è un'istanza dell'interfaccia SelectableDataModel. Almeno due classi implementano questa interfaccia: ListDataModel, il cui costruttore accetta una lista come parametro, e ArrayDataModel, il cui costruttore accetta un array come parametro. Qui, 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 identifica in modo univoco una persona, in questo caso l'ID della persona,
  • riga 23: il metodo getRowData deve restituire l'oggetto selezionato in base al suo rowkey. In questo caso, quindi, restituisce 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,
  • righe 24–35: restituiscono il riferimento alla persona il cui ID è stato ricevuto. Questo riferimento verrà assegnato al modello [Form].personneChoisie. Il metodo [retirerPersonne] rimane quindi invariato:

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

Questa è la tecnica da utilizzare quando la relazione tra gli attributi **rowkey e **selection non è una semplice relazione proprietà-oggetto (**rowkey** a **selection**).

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

Questo progetto è simile al precedente, tranne per il fatto che cambia il metodo di selezione della persona da rimuovere:

Image

Sopra, vediamo che l'oggetto viene selezionato tramite un menu contestuale (clic destro). Viene richiesta una conferma dell'eliminazione:

 

Il modulo [index.xhtml] cambia come segue:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
 
      <!-- title -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
 
      <!-- contextual menu -->
      <p:contextMenu for="personnes">  
        <p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
      </p:contextMenu>  
 
      <!-- dialog box -->
      <p:confirmDialog widgetVar="confirmation" message="#{msg['form.suppression.confirmation']}"  
                       header="#{msg['form.suppression.message']}" severity="alert" >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu"                          action="#{form.retirerPersonne}" oncomplete="confirmation.hide()"/>
        <p:commandButton value="#{msg['form.supprimer.non']}" onclick="confirmation.hide()" type="button" />                
      </p:confirmDialog>  
 
      <!-- dataTable-->
      <p:dataTable id="personnes" value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
                   selection="#{form.personneChoisie}" selectionMode="single">
        ...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • righe 9–11: definiscono un menu contestuale per (attributo for) la dataTable alla riga 21 (attributo id). Questo menu contestuale appare quando si fa clic con il tasto destro sulla tabella delle persone,
  • riga 10: il nostro menu ha una sola opzione (tag menuItem). Quando si fa clic su questa opzione, viene eseguito il codice JavaScript nell'attributo onclick. Il codice JavaScript [confirmation.show()] visualizza la finestra di dialogo della riga 14 (attributo widgetVar). È il seguente:
  • riga 14: l'attributo message visualizza [3], l'attributo header visualizza [1], l'attributo severity visualizza l'icona [2],
  • riga 16: visualizza [4]. Facendo clic, 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 eseguita,
  • riga 17: visualizza [5]. Quando si fa clic, la finestra di dialogo si chiude e la persona non viene eliminata.

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

Questo progetto dimostra che è possibile ricevere una risposta dal server dopo aver eseguito una chiamata AJAX. Per farlo, utilizziamo l'attributo oncomplete della chiamata AJAX:

 

Il modulo [index.xhtml] cambia come segue:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
...
<!-- dialog box 1 -->
      <p:confirmDialog widgetVar="confirmation" ... >                   
        <p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu" action="#{form.retirerPersonne}" oncomplete="handleRequest(xhr, status, args);confirmation.hide()"/>
        <p:commandButton ... />                
      </p:confirmDialog>  
 
      <!-- Javascript -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // erreur ?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
      </script> 
...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • riga 7: l'attributo oncomplete chiama la funzione JavaScript nelle righe 13–18,
  • riga 13: la firma del metodo deve essere questa. args è un dizionario che il modello lato server può popolare,
  • riga 15: controlliamo se il dizionario args ha un attributo denominato 'msgError'. In tal caso, viene visualizzato (riga 16).

Nel modello, il metodo [removePerson] si evolve come segue:


public void retirerPersonne() {
    // suppression aléatoire
    int i = (int) (Math.random() * 2);
    if (i == 0) {
      // on enlève la personne choisie
      personnes.remove(personneChoisie);
    } else {
      // on renvoie une erreur
      String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
      RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
    }
  }
  • riga 3: genera 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 aggiunge l'attributo denominato 'msgError' al dizionario args menzionato in precedenza, con il valore di msgError costruito alla riga 9. Questo attributo viene poi recuperato dal metodo JavaScript in [index.xhtml]:

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

5.18. Esempio: mv-pf-15: la barra degli strumenti

In questo progetto, stiamo realizzando una barra degli strumenti:

La barra degli strumenti è il componente mostrato nel riquadro qui sopra. È stata creata utilizzando il seguente codice XHTML [index.xhtml]:


 
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
      <!-- title -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>
 
      <!-- toolbar-->
      <p:toolbar>
        <p:toolbarGroup align="left">  
          ...  
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          ...  
        </p:toolbarGroup>  
      </p:toolbar>
    </ui:define>
  </ui:composition>
</html>
  • righe 15–22: la barra degli strumenti,
  • righe 16–18: definiscono il gruppo di componenti a sinistra della barra degli strumenti,
  • 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 qui sotto:

  • riga 2: visualizza [1],
  • riga 3: visualizza uno spazio di 30 pixel [2],
  • righe 4–6: visualizza un menu a tendina con un elenco di persone [3],
  • riga 7: visualizza un separatore [4],
  • riga 8: visualizza un pulsante [5] utilizzato per eliminare la persona selezionata dall'elenco a discesa. Il pulsante presenta un'icona. Queste icone provengono da jQuery UI. È possibile trovare un elenco delle stesse all'URL [http://jqueryui.com/themeroller/] [6]:
  • per trovare il nome di un'icona, è sufficiente passarci sopra con il mouse. Questo nome viene poi utilizzato nell'attributo icon del componente <commandButton>, ad esempio icon="ui-icon-trash". Si noti che sopra il nome assegnato è .ui-icon-trash e che il punto iniziale viene rimosso da questo nome nell'attributo icon,
  • Riga 9: crea un tooltip per il pulsante (per l'attributo). Quando si passa il mouse sul pulsante, appare il messaggio del tooltip [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 {
 
  // model
  private List<Personne> personnes;
  private int personneId;
 
  // manufacturer
  public Form() {
    // initialization of the list of persons
    personnes = new ArrayList<Personne>();
    personnes.add(new Personne(1, "dupont", "jacques"));
    personnes.add(new Personne(2, "durand", "élise"));
    personnes.add(new Personne(3, "martin", "jacqueline"));
  }
 
  public void supprimerPersonne() {
    // search for the selected person
    int i = 0;
    for (Personne personne : personnes) {
      // current person = selected person?
      if (personne.getId() == personneId) {
        // delete the current person from the list
        personnes.remove(i);
        // we're done
        break;
      } else {
        // next person
        i++;
      }
    }
  }
 
  // getters and setters
  ...
}

I componenti sul lato destro 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>

Visualizzano la vista riportata di seguito:

 
  • righe 6–9: un pulsante del menu. Contiene le opzioni del menu,
  • riga 7: l'opzione per passare alla versione francese del modulo,
  • riga 8: l'opzione per passare all'inglese.

5.19. Conclusione

Ora sappiamo abbastanza per trasferire la nostra applicazione di esempio su PrimeFaces. Abbiamo trattato solo una quindicina di componenti, mentre la libreria ne contiene oltre 100. Invitiamo i lettori a cercare eventuali componenti mancanti direttamente sul sito web di PrimeFaces.

5.20. Test con Eclipse

I progetti Maven sono disponibili sul sito web degli esempi [1]:

Una volta importati in Eclipse, possono essere eseguiti [2]. Selezionare Tomcat in [3]. Verranno quindi visualizzati nel browser interno di Eclipse [3].