Skip to content

5. Einführung in die PrimeFaces-Komponentenbibliothek

5.1. Die Rolle von PrimeFaces in einer JSF-Anwendung

Kehren wir zur Architektur einer JSF-Anwendung zurück, wie wir sie zu Beginn dieses Dokuments behandelt haben:

JSF-Seiten wurden unter Verwendung von drei Tag-Bibliotheken erstellt:

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">
  • Zeile 2: die <h:x>-Tags aus dem Namespace [http://java.sun.com/jsf/html], die HTML-Tags entsprechen,
  • Zeile 3: die <f:y>-Tags im Namensraum [http://java.sun.com/jsf/core], die JSF-Tags entsprechen,
  • Zeile 4: die <ui:z>-Tags aus dem Namensraum [http://java.sun.com/jsf/facelets], die Facelet-Tags entsprechen.

Um JSF-Seiten zu erstellen, fügen wir eine vierte Tag-Bibliothek hinzu, nämlich die der PrimeFaces-Komponenten.

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
      ...
xmlns:p="http://primefaces.org/ui">
  • Zeile 3: Die <p:z>-Tags aus dem Namespace [http://primefaces.org/ui] entsprechen PrimeFaces-Komponenten.

Dies ist die einzige Änderung, die vorgenommen wird. Sie erscheint daher in den Ansichten. Ereignisbehandler und Modelle bleiben unverändert gegenüber JSF. Dies ist ein wichtiger Punkt, den es zu verstehen gilt.

Die Verwendung von PrimeFaces-Komponenten ermöglicht es Ihnen, Weboberflächen zu erstellen, die dank der vielen Komponenten in dieser Bibliothek benutzerfreundlicher und dank der nativ unterstützten AJAX-Technologie flüssiger sind. Diese werden als Rich Interfaces oder RIAs (Rich Internet Applications) bezeichnet.

Die bisherige JSF-Architektur wird zur folgenden PF-Architektur (PrimeFaces):

5.2. Die Vorteile von PrimeFaces

Die PrimeFaces-Website [http://www.primefaces.org/showcase/ui/home.jsf] enthält eine Liste der Komponenten, die in einer PF-Seite verwendet werden können:

In den folgenden Beispielen werden wir die ersten beiden Funktionen von PrimeFaces verwenden:

  • einige der rund hundert angebotenen Komponenten
  • ihr natives AJAX-Verhalten.

Zu den verfügbaren Komponenten gehören:

Wir werden in unseren Beispielen nur etwa fünfzehn davon verwenden, aber das reicht aus, um die Grundlagen der Erstellung einer PrimeFaces-Seite zu verstehen.

5.3. PrimeFaces lernen

PrimeFaces bietet Anwendungsbeispiele für jede seiner Komponenten. Klicken Sie einfach auf den Link. Sehen wir uns ein Beispiel an:

  • in [1] das Beispiel für die [Spinner]-Komponente,
  • in [2] das Dialogfeld, das nach dem Klicken auf die Schaltfläche [Submit] angezeigt wird.

Hier gibt es drei neue Funktionen für uns:

  • die [Spinner]-Komponente, die in JSF standardmäßig nicht vorhanden ist,
  • das Gleiche gilt für das Dialogfeld,
  • und schließlich wird der durch die Schaltfläche [Submit] ausgelöste POST-Aufruf über AJAX abgewickelt. Wenn Sie den Browser während des POST-Aufrufs genau beobachten, sehen Sie keine Sanduhr. Die Seite wird nicht neu geladen. Sie wird lediglich geändert: Eine neue Komponente – in diesem Fall das Dialogfeld – erscheint auf der Seite.

Schauen wir uns an, wie das alles funktioniert. Der XHTML-Code für das Beispiel lautet wie folgt:


<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>

Zunächst ist zu beachten, dass wir Standard-JSF-Tags sehen: <h:form> in Zeile 1, <h:panelGrid> in Zeile 3, <h:outputLabel> in Zeile 4. Einige JSF-Tags werden von PF wiederverwendet und erweitert: <p:commandButton> in Zeile 21. Als Nächstes finden wir PF-Formatierungstags: <p:panel> in Zeile 2, <p:outputPanel> in Zeile 13, <p:dialog> in Zeile 23. Schließlich haben wir Eingabetags: <p:spinner> in Zeile 5.

Lassen Sie uns diesen Code im Zusammenhang mit der Ansicht analysieren:

  • in [1] die mit dem <p:panel>-Tag in Zeile 2 erstellte Komponente,
  • in [2] das Eingabefeld, das durch die Kombination der Tags <p:outputLabel> und <p:spinner> in den Zeilen 6 und 7 erstellt wurde,
  • in [3] die POST-Schaltfläche, die mit dem <p:commandButton>-Tag in Zeile 21 erstellt wurde,
  • in [4] das Dialogfeld aus den Zeilen 23–25,
  • in [5] ein unsichtbarer Container für zwei Komponenten. Er wird durch das <p:outputPanel>-Tag in Zeile 13 erstellt.

Betrachten wir den folgenden Code, der eine AJAX-Aktion implementiert:


           <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>

Dieser Code erzeugt die folgende Ansicht:

  • Zeile 1: Zeigt den Text [1] an. Sie dient auch als Bezeichnung für die Komponente mit der ID „ajaxspinner“ (für das Attribut). Diese Komponente ist die in Zeile 3 (ID-Attribut),
  • Zeilen 3–5: zeigen die Komponente [2] an. Diese Komponente ist eine Eingabe-/Anzeigekomponente, die mit dem Modell #{spinnerController.number5} verknüpft ist (Wert-Attribut),
  • Zeile 6: Zeigt die Komponente [3] an. Diese Komponente ist eine Anzeigekomponente, die mit dem Modell #{spinnerController.number5} verknüpft ist (Wert-Attribut),
  • Zeile 4: Das <p:ajax>-Tag fügt dem Spinner AJAX-Funktionalität hinzu. Jedes Mal, wenn der Spinner seinen Wert ändert, wird eine POST-Anfrage mit diesem Wert (Attribut „process="@this"“) an das Modell #{spinnerController.number5} gesendet. Sobald dies geschehen ist, wird die Seite aktualisiert (Attribut „update“). Der Wert dieses Attributs ist die ID einer Komponente auf der Seite, in diesem Fall die in Zeile 6. Die vom Attribut „update“ angegebene Komponente wird dann mit dem Modell aktualisiert. Dies ist wiederum #{spinnerController.number5}, d. h. der Wert des Spinners. Somit folgt Feld [3] den Einträgen in Feld [2].

Dies ist ein AJAX-Verhalten, eine Abkürzung für Asynchronous JavaScript and XML. Im Allgemeinen funktioniert ein AJAX-Verhalten wie folgt:

  • Der Browser zeigt eine HTML-Seite an, die JavaScript-Code enthält (das „J“ in AJAX). Die Elemente der Seite bilden ein JavaScript-Objekt, das als DOM (Document Object Model) bezeichnet wird,
  • der Server hostet die Webanwendung, die diese Seite generiert hat,
  • in [1] tritt ein Ereignis auf der Seite ein. Beispielsweise wird der Ladekreis erhöht. Dieses Ereignis wird von JavaScript verarbeitet,
  • in [2] sendet das JavaScript eine POST-Anfrage an die Webanwendung. Dies geschieht asynchron (das „A“ in AJAX). Der Benutzer kann weiter mit der Seite arbeiten. Sie ist nicht eingefroren, kann aber bei Bedarf eingefroren werden. Der POST-Befehl aktualisiert das Seitenmodell basierend auf den übermittelten Werten, hier das Modell #{spinnerController.number5},
  • in [3] sendet die Webanwendung eine XML- (das X in AJAX) oder JSON- (JavaScript Object Notation) Antwort zurück an JavaScript,
  • in [4] verwendet JavaScript diese Antwort, um einen bestimmten Bereich des DOM zu aktualisieren, hier den Bereich mit der ID „ajaxspinnervalue“.

Bei der Verwendung von JSF und PrimeFaces wird das JavaScript von PrimeFaces generiert. Diese Bibliothek stützt sich auf die jQuery-JavaScript-Bibliothek. Ebenso stützen sich PrimeFaces-Komponenten auf diejenigen aus der jQuery-UI-Komponentenbibliothek (User Interface). Daher bildet jQuery die Grundlage von PrimeFaces.

Kehren wir zu unserem Beispiel zurück und betrachten wir nun die POST-Anfrage über die Schaltfläche [Submit]:

Der mit dem POST verbundene Code lautet wie folgt:


<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>
  • Zeile 1: Die POST-Anfrage wird durch die Schaltfläche in Zeile 1 ausgelöst. In PrimeFaces lösen Tags, die eine POST-Anfrage auslösen, diese standardmäßig über einen AJAX-Aufruf aus. Aus diesem Grund verfügen diese Tags über ein `update`-Attribut, mit dem der Bereich angegeben wird, der nach Erhalt der Serverantwort aktualisiert werden soll. In diesem Fall ist der zu aktualisierende Bereich das panelGrid in Zeile 4. Sobald die POST-Antwort zurückkommt, wird dieser Bereich also mit den an das Modell übermittelten Werten aktualisiert. Diese Werte sind jedoch in einem Dialogfeld enthalten, das standardmäßig ausgeblendet ist. Es ist das oncomplete-Attribut in Zeile 1, das es anzeigt. Dieses Ereignis tritt am Ende der POST-Verarbeitung auf. Der Wert dieses Attributs ist JavaScript-Code. Hier zeigen wir das Dialogfeld mit id=dialog an, das aus Zeile 3 stammt (widgetVar-Attribut),
  • Zeile 3: Wir sehen verschiedene Attribute des Dialogfelds. Sie müssen ein wenig experimentieren, um herauszufinden, was sie bewirken.

Wir haben das Modell erwähnt, es aber noch nicht gezeigt. Hier ist es:

public class SpinnerController {

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

    // getters and setters
...
}

Im Allgemeinen können Sie wie folgt vorgehen:

  • Identifizieren Sie die PrimeFaces-Komponente, die Sie verwenden möchten,
  • sehen Sie sich das dazugehörige Beispiel an. Die PrimeFaces-Beispiele sind gut geschrieben und leicht verständlich.

5.4. Ein erstes PrimeFaces-Projekt: mv-pf-01

Erstellen wir ein Maven-Webprojekt mit NetBeans:

  • [1, 2, 3]: Erstellen Sie ein Maven-Projekt vom Typ [Webanwendung],
  • [4]: Als Server wird Tomcat verwendet,
  • in [5] das generierte Projekt,
  • in [6] die Datei [index.jsp] und das Java-Paket entfernen,
  • in [7, 8]: In den Projekteigenschaften fügen wir Unterstützung für Java Server Faces hinzu,
  • in [9], wählen Sie auf der Registerkarte [Komponenten] die PrimeFaces-Komponentenbibliothek aus. NetBeans bietet Unterstützung für weitere Komponentenbibliotheken: ICEFaces und RichFaces.
  • In [10] das generierte Projekt. Beachten Sie in [11] die Abhängigkeit von PrimeFaces.

Kurz gesagt ist ein PrimeFaces-Projekt ein Standard-JSF-Projekt, dem eine Abhängigkeit zu PrimeFaces hinzugefügt wurde. Mehr nicht.

Nachdem wir dies nun verstanden haben, ändern wir die Datei [pom.xml], damit sie mit den neuesten Versionen der Bibliotheken funktioniert:


    <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>

Zeilen 26–30: Beachten Sie das Maven-Repository für PrimeFaces. Sobald diese Änderungen vorgenommen wurden, erstellen Sie das Projekt, um mit dem Herunterladen der Abhängigkeiten zu beginnen. Sie erhalten dann das Projekt [12].

Versuchen wir nun, das Beispiel, das wir behandelt haben, nachzubilden. Die Seite [index.html] sieht nun wie folgt aus:


<?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>

Vergessen Sie nicht Zeile 5, in der der Namespace für die PrimeFaces-Tag-Bibliothek deklariert wird. Fügen Sie dem Projekt die Bean hinzu, die als Vorlage für die Seite dient:

  

Der Code lautet wie folgt:


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
  ...
}

Die Klasse ist eine Bean (Zeile 6) mit Request-Gültigkeitsbereich (Zeile 7). Da kein Name angegeben wurde, erhält die Bean den Namen der Klasse, wobei das erste Zeichen klein geschrieben wird: spinnerController.

Wenn wir das Projekt ausführen, erhalten wir Folgendes:

 

Wir haben soeben gezeigt, wie man ein Beispiel aus der PrimeFaces-Website testet. Alle Beispiele können auf diese Weise getestet werden.

Im weiteren Verlauf werden wir uns nur auf bestimmte PrimeFaces-Komponenten konzentrieren. Zunächst werden wir die mit JSF behandelten Beispiele noch einmal durchgehen und bestimmte JSF-Tags durch PrimeFaces-Tags ersetzen. Das Erscheinungsbild der Seiten wird sich leicht ändern; sie werden AJAX-Funktionen aufweisen, aber die zugehörigen Beans müssen nicht geändert werden. In jedem der folgenden Beispiele werden wir lediglich den XHTML-Code der Seiten und die dazugehörigen Screenshots präsentieren. Leser sind aufgefordert, die Beispiele zu testen, um die Unterschiede zwischen den JSF-Seiten und den PF-Seiten zu erkennen.

5.5. Beispiel mv-pf-02: Ereignisbehandler – Internationalisierung – Seitennavigation

Dieses Projekt ist eine Portierung des JSF-Projekts [mv-jsf2-02] (Abschnitt 2.4, Seite 41):

Das NetBeans-Projekt sieht wie folgt aus:

Die Seite [index.html] sieht wie folgt aus:


<?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>

In den Zeilen 15, 16 und 19 wurden die <h:commandLink>-Tags durch <p:commandLink>-Tags ersetzt. Dieses Tag verfügt standardmäßig über AJAX-Funktionalität, die durch Setzen des Attributs ajax="false" deaktiviert werden kann. Hier verhalten sich die <p:commandLink>-Tags also wie <h:commandLink>-Tags: Die Seite wird neu geladen, wenn auf diese Links geklickt wird.

5.6. Beispiel mv-pf-03: Seitenlayout mit Facelets

Dieses Projekt demonstriert die Erstellung von XHTML-Seiten unter Verwendung der Facelets-Vorlagen aus Beispiel [mv-jsf2-09] (Abschnitt 2.11):

 

Das NetBeans-Projekt sieht wie folgt aus:

  • in [1], die JSF-Projektkonfigurationsdateien,
  • in [2], die XHTML-Seiten,
  • in [3] die Support-Bean für den Sprachwechsel,
  • in [4] die Nachrichtendateien,
  • in [5] die Abhängigkeiten.

Die Projektseiten basieren auf der Seite [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>
  • Zeile 9: Ein <f:view>-Tag umschließt die gesamte Seite, um die damit verbundene Internationalisierung zu nutzen,
  • Zeile 15: ein Formular mit der Formular-ID. Dieses Formular bildet den Hauptteil der Seite. Innerhalb dieses Hauptteils gibt es nur einen dynamischen Abschnitt, die Zeilen 28–30. Hier wird der variable Teil der Seite eingefügt:
  • Der oben eingerahmte Bereich wird über AJAX-Aufrufe aktualisiert. Um ihn zu identifizieren, haben wir ihn in einen PrimeFaces-Container eingefügt, der durch das <p:outputPanel>-Tag (Zeile 27) generiert wurde. Dieser Container wurde als `content` (id-Attribut) benannt. Da er sich innerhalb eines Formulars befindet, das selbst ein Container mit dem Namen `form` ist, lautet der vollständige Name des dynamischen Bereichs `form:content`. Der erste Doppelpunkt (:) gibt an, dass wir vom Stamm des Dokuments ausgehen, dann in den Container mit dem Namen „form“ und anschließend in den Container mit dem Namen „content“ wechseln. Eine Herausforderung bei AJAX besteht darin, die Bereiche, die durch einen AJAX-Aufruf aktualisiert werden sollen, korrekt zu benennen. Am einfachsten ist es, den Quellcode der empfangenen HTML-Seite zu betrachten:

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

Oben sehen wir, dass das <h:outputPanel>-Tag ein HTML-Tag <span> generiert hat. In diesem Beispiel beziehen sich der relative Name form:content (ohne den führenden Doppelpunkt) und der vollständige Name :form:content (mit dem führenden Doppelpunkt) auf dasselbe Objekt.

Beachten Sie, dass AJAX-Aufrufe (<p:commandButton>, <p:commandLink>), die den dynamischen Bereich aktualisieren, das Attribut update=":form:content" haben.

Die Seite [index.xhtml] ist die einzige Seite, die vom Projekt angezeigt wird:


<?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>
  • Zeile 8: Die Vorlage für [index.xhtml] ist die soeben besprochene Seite [layout.xhtml].
  • Zeile 9: Dies ist der Inhalts-ID-Bereich, der von [index.html] aktualisiert wird. In diesem Bereich befinden sich zwei Fragmente:
    • das Fragment [page1.xhtml] in Zeile 11;
    • das Fragment [page2.xhtml] in Zeile 14.

Diese beiden Fragmente schließen sich gegenseitig aus.

  • Zeile 10: Das Fragment [page1.xhtml] wird angezeigt, wenn in der Anfrage das Attribut page1 auf true gesetzt ist oder wenn das Attribut page2 nicht vorhanden ist. Dies ist bei der allerersten Anfrage der Fall, bei der keines dieser Attribute in der Anfrage vorhanden ist. In diesem Fall wird das Fragment [page1.xhtml] angezeigt.
  • Zeile 11: Das Fragment [page2.xhtml] wird angezeigt, wenn in der Anfrage das Attribut „page2“ auf „true“ gesetzt ist

Das Fragment [page1.xhtml] lautet wie folgt:


<?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>

und zeigt folgenden Inhalt an:

  • Zeilen 11 und 12: die beiden Links zum Ändern der Sprache. Diese beiden Links lösen AJAX-Aufrufe aus (ajax=true). Dies ist der Standardwert. Daher müssen Sie das Attribut ajax=true nicht angeben. Wir werden dies in Zukunft auch nicht tun. Beachten Sie, dass diese beiden Links den Bereich :form:content (Attribut update) aktualisieren, der oben hervorgehoben ist,
  • Zeile 15: ein AJAX-Navigationslink, der wiederum den Bereich :form:content aktualisiert,
  • Zeile 16: Wir verwenden das Tag <h:setPropertyActionListener>, um das Attribut „page2“ in der Anfrage auf „true“ zu setzen. Dadurch wird das Fragment [page2.xhtml] (Zeile 6 unten) auf der Seite [index.xhtml] angezeigt:

  <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>

Das Fragment [page2.xhtml] ist ähnlich:

Der Code für [page2.xhtml] lautet wie folgt:


<?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>

Aus diesem Beispiel behalten wir die folgenden Punkte für später im Hinterkopf:

  • Wir verwenden die Vorlage [layout.xhtml] als Seitenvorlage,
  • der dynamische Bereich wird durch die ID: form:content gekennzeichnet und über AJAX-Aufrufe aktualisiert.

5.7. Beispiel mv-pf-04: Eingabeformular

Dieses Projekt ist eine Portierung des JSF2-Projekts [mv-jsf2-03] (siehe Abschnitt 2.5):

Das NetBeans-Projekt sieht wie folgt aus:

Oben, unter [1], befinden sich die XHTML-Seiten des Projekts. Das Layout wird durch die zuvor besprochene Vorlage [layout.xhtml] bereitgestellt. Die Seite [index.xhtml] ist die einzige Seite des Projekts. Sie wird im Bereich :form:content angezeigt. Ihr Code lautet wie folgt:


<?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>

Es zeigt einfach das Fragment [page1.xhtml] an. Dies entspricht dem im Beispiel [mv-jsf2-03] behandelten Formular. Erinnern Sie sich daran, dass der Zweck dieses Beispiels darin bestand, JSF-Eingabetags vorzustellen. Diese Tags wurden hier durch PrimeFaces-Tags ersetzt.

PanelGrid

Um die Elemente in [page1.xhtml] zu formatieren, verwenden wir das <p:panelGrid>-Tag. Zum Beispiel für die beiden Sprachlinks:


<!-- 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>

Dies erzeugt die folgende Ausgabe:

 

Eine weitere Form des <p:panelGrid>-Tags sieht wie folgt aus:


<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>

Die Zeilen und Spalten der Tabelle werden durch die Tags <p:row> und <p:column> gekennzeichnet.

Die Zeilen 3–12 definieren die Tabellenüberschrift:

 

Die Zeilen 14–25 definieren eine Zeile der Tabelle:

 

Die Zeilen 27–35 definieren die Tabellenfußzeile:

 

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>
 

Passwort


<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>

Zeile 7: Das Attribut „feedback=true“ gibt Auskunft über die Stärke [1] des Passworts.

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>

Zeile 7: Das <p:editor>-Tag zeigt einen Rich-Text-Editor an, mit dem Sie den Text formatieren können (Schriftart, Größe, Farbe, Ausrichtung usw.). An den Server wird der HTML-Code des eingegebenen Textes gesendet [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>
 

Zeile 14: Beachten Sie, dass der Link [Reset] eine AJAX-Aktualisierung des Feldes :form:selectManyMenu durchführt, das der Komponente in Zeile 6 entspricht. Es ist jedoch wichtig zu beachten, dass während des AJAX-POST-Aufrufs alle Formularwerte übermittelt werden. Daher wird das gesamte Modell aktualisiert. Bei diesem Modell wird jedoch nur das Feld :form:selectManyMenu aktualisiert.

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. Beispiel: mv-pf-05: dynamische Listen

Dieses Projekt ist eine Portierung des JSF2-Projekts [mv-jsf2-04] (siehe Abschnitt 2.6):

Image

Dieses Projekt führt im Vergleich zum vorherigen Projekt keine neuen PrimeFaces-Tags ein. Daher werden wir es nicht näher erläutern. Es ist in der Liste der Beispiele enthalten, die dem Leser auf der Website des Dokuments zur Verfügung stehen.

5.9. Beispiel: mv-pf-06: Navigation – Sitzung – Ausnahmebehandlung

Dieses Projekt ist eine Portierung des JSF2-Projekts [mv-jsf2-05] (siehe Abschnitt 2.7):

Auch in diesem Beispiel werden keine neuen PrimeFaces-Tags eingeführt. Wir werden lediglich auf die oben hervorgehobene Tabelle mit Links eingehen:


<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>
  • Alle Links haben das Attribut ajax=false. Daher wird die Seite normal geladen.
  • Beachten Sie die Zeilen 2 und 4, um zu sehen, wie eine Weiterleitung durchgeführt wird.

5.10. Beispiel: mv-pf-07: Validierung und Konvertierung von Eingabedaten

Dieses Projekt ist eine Portierung des JSF2-Projekts [mv-jsf2-06] (siehe Abschnitt 2.8):

Image

Die Anwendung führt zwei neue Tags ein, das <p:messages>-Tag:


<p:messages globalOnly="true"/>

Image

und das <p:message>-Tag:


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

Im Vergleich zum <h:message>-Tag von JSF führt das <p:message>-Tag von PF folgende Änderungen ein:

  • Das Erscheinungsbild der Fehlermeldung ist anders [1],
  • das fehlerhafte Eingabefeld ist von einem roten Rahmen umgeben [2].

5.11. Beispiel: mv-pf-08: Ereignisse im Zusammenhang mit Änderungen des Komponentenstatus

Dieses Projekt ist eine Portierung des JSF2-Projekts [mv-jsf2-07] (siehe Abschnitt 2.9):

Image

Das JSF-Projekt führte das Konzept der Listener ein. Die Verwaltung von Listenern wurde bei PrimeFaces anders gehandhabt.

Bei 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}"/>

Mit 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>
  • Zeile 2: Das <h:selectOneMenu>-Tag ohne das valueChangeListener-Attribut,
  • Zeile 4: Das <p:ajax>-Tag fügt dem übergeordneten <h:selectOneMenu>-Tag AJAX-Funktionalität hinzu. Standardmäßig reagiert es auf das Ereignis „value change“ der Liste combo1. Bei diesem Ereignis werden die Werte des Formulars, zu dem es gehört, über einen AJAX-Aufruf an den Server gesendet. Das Modell wird somit aktualisiert. Wir verwenden dieses neue Modell, um die als combo2 bezeichnete Dropdown-Liste zu aktualisieren (Zeile 10). Beachten Sie in Zeile 4, dass der AJAX-Aufruf keine Modellmethoden ausführt. Dies ist hier nicht erforderlich. Wir möchten lediglich das Modell aktualisieren, indem wir die eingegebenen Werte übermitteln.

5.12. Beispiel: mv-pf-09: Unterstützte Eingabe

Dieses Projekt enthält PrimeFaces-spezifische Eingabe-Tags, die die Eingabe bestimmter Datentypen erleichtern:

5.12.1. Das NetBeans-Projekt

Das NetBeans-Projekt sieht wie folgt aus:

Der Wert des Projekts liegt in:

  • der einzelnen Seite [index.html], die es anzeigt,
  • der Vorlage [Form.java] für diese Seite.

5.12.2. Die Vorlage

Das Formular verfügt über vier Eingabefelder, die mit der folgenden Vorlage verknüpft sind:


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
...
}

Die vier Eingaben sind den Feldern in den Zeilen 14–17 zugeordnet.

5.12.3. Das Formular

Das Formular sieht wie folgt aus:


<?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>

Werfen wir einen Blick auf die vier Eingabefelder.

5.12.4. Der Kalender

Mit dem Tag <p:calendar> können Sie ein Datum aus einem Kalender auswählen. Dieses Tag unterstützt verschiedene Attribute.


<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>

Zeile 2 legt fest, dass das Datum im Format „dd/mm/yyyy“ angezeigt werden soll und dass die Zeitzone Paris ist. Wenn Sie den Cursor in das Eingabefeld setzen, wird ein Kalender angezeigt:

 

5.12.5. Der Schieberegler

Mit dem <p:slider>-Tag können Sie eine ganze Zahl eingeben, indem Sie einen Schieberegler entlang einer Leiste ziehen:

 

Der Tag-Code lautet wie folgt:


        <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}"/>
  • Zeile 3: Dies ist ein Standard-<p:inputText>-Tag, mit dem Sie eine ganze Zahl eingeben können. Dieser Wert kann auch über den Schieberegler eingegeben werden,
  • Zeile 4: Das <p:slider>-Tag ist mit dem <p:inputText>-Eingabetag verknüpft (für das Attribut). Wir legen einen Mindest- und einen Höchstwert dafür fest.

5.12.6. Der Spinner

Wir haben diese Komponente bereits vorgestellt:


        <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}"/>

Zeile 3: Über das Eingabefeld können Sie eine ganze Zahl zwischen 1 und 12 eingeben. Sie können die Zahl direkt in das Eingabefeld eingeben oder die Pfeile verwenden, um den Wert zu erhöhen oder zu verringern.

 

5.12.7. Automatische Vervollständigung

Bei der Autovervollständigung gibt man die ersten paar Zeichen der Eingabe ein. Daraufhin erscheinen Vorschläge in einer Dropdown-Liste. Sie können einen davon auswählen. Diese Komponente wird anstelle von Dropdown-Listen verwendet, wenn diese zu viele Einträge enthalten. Angenommen, Sie möchten eine Dropdown-Liste mit Städten in Frankreich anbieten. Das sind mehrere tausend Städte. Wenn Sie den Benutzer die ersten drei Zeichen des Stadtnamens eingeben lassen, können Sie ihm eine reduzierte Liste von Städten anbieten, die mit diesen Zeichen beginnen.

 

Der Code für diese Komponente lautet wie folgt:


        <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/>

Das <p:autoComplete>-Tag in Zeile 2 ermöglicht die Autovervollständigung. Der hier relevante Parameter ist das Attribut completeMethod, dessen Wert der Name einer Methode im Modell ist, die für die Generierung von Vorschlägen auf der Grundlage der vom Benutzer eingegebenen Zeichen zuständig ist. Diese Methode ist wie folgt definiert:


  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;
}

  • Zeile 1: Die Methode erhält als Parameter die Zeichenfolge, die der Benutzer in das Eingabefeld eingegeben hat. Sie gibt eine Liste mit Vorschlägen zurück,
  • Zeilen 4–6: Es wird eine Liste mit 10 Vorschlägen erstellt, wobei die als Parameter erhaltenen Zeichen verwendet und jeweils eine Ziffer von 0 bis 9 hinzugefügt wird.

5.12.8. Das <p:growl>-Tag

Das <p:growl>-Tag ist ein möglicher Ersatz für das <p:messages>-Tag, das Formularfehlermeldungen anzeigt.


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

Im obigen Beispiel wird das id-Attribut nicht verwendet. Das Attribut autoUpdate=true gibt an, dass die Liste der Fehlermeldungen bei jedem Formular-POST aktualisiert werden muss.

Angenommen, wir senden das folgende Formular ab [1]:

  • In [2] zeigt das <p:growl>-Tag dann die Fehlermeldungen an, die mit den fehlerhaften Eingaben verbunden sind.

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

Dieses Projekt stellt das <p:dataTable>-Tag vor, das zur Anzeige von Datenlisten verwendet wird

Image

5.13.1. Das NetBeans-Projekt

Das NetBeans-Projekt sieht wie folgt aus:

Der Reiz des Projekts liegt in:

  • der vom Projekt angezeigten einzelnen Seite [index.html],
  • der [Form.java]-Vorlage für diese Seite und dem [Person]-Bean.

5.13.2. Die Meldungsdatei

Die Datei [messages_fr.properties] sieht wie folgt aus:


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. Das Modell

Die [Person]-Bean repräsentiert eine Person:


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
...
}

Die Vorlage für die Seite [index.xhtml] ist die folgende [Form]-Klasse:


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
...  
}
  • Zeilen 9–10: Die Bean hat den Gültigkeitsbereich „session“,
  • Zeilen 18–24: Der Konstruktor erstellt eine Liste mit drei Personen, die über mehrere Anfragen hinweg bestehen bleibt,
  • Zeile 15: die ID einer Person, die aus der Liste entfernt werden soll,
  • Zeilen 26–28: die delete-Methode.

5.13.4. Das Formular

Das Formular sieht wie folgt aus [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>

Dies erzeugt die folgende Ansicht (siehe Kasten unten):

  • Zeile 12: Erzeugt die im obigen Kasten gezeigte Tabelle. Das Attribut „value“ gibt die von der Tabelle angezeigte Sammlung an, in diesem Fall die Liste der Personen aus dem Modell. Das Attribut „emptyMessage“ ist optional. Es gibt die Meldung an, die angezeigt werden soll, wenn die Liste leer ist. Standardmäßig lautet sie „keine Datensätze gefunden“. Hier lautet sie:
 
  • Zeilen 13–15: Kopfzeile [1] generieren,
  • Zeilen 16–21: Spalte [2] generieren,
  • Zeilen 22–27: Spalte [3] generieren,
  • Zeilen 28–33: Spalte [4] generieren,
  • Zeilen 34–41: Spalte [5] generieren.

Über den Link [Entfernen] können Sie eine Person aus der Liste entfernen. In Zeile [38] führt die Methode [Form].removePerson diese Aufgabe aus. Sie benötigt die ID der zu entfernenden Person. Diese wird in Zeile 39 bereitgestellt. In Zeile 38 haben wir das action-Attribut verwendet. In anderen Fällen haben wir das actionListener-Attribut verwendet. Ich bin mir nicht sicher, ob ich den funktionalen Unterschied zwischen diesen beiden Attributen vollständig verstehe. In der Praxis stellen wir jedoch fest, dass die durch die <setPropertyActionListener>-Tags festgelegten Attribute gesetzt werden, bevor die durch das action-Attribut angegebene Methode ausgeführt wird, während dies beim actionListener-Attribut nicht der Fall ist. Kurz gesagt: Sobald Parameter an die aufgerufene Aktion gesendet werden müssen, musst du das action-Attribut verwenden.

Die Methode zum Entfernen einer Person lautet wie folgt:


...
@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. Beispiel: mv-pf-11: dataTable - 2

Dieses Projekt enthält eine Tabelle, in der eine Liste von Daten angezeigt wird, aus der eine Zeile ausgewählt werden kann:

Durch die Auswahl einer Zeile in der Tabelle werden während der POST-Anfrage Informationen über die ausgewählte Zeile an das Modell gesendet. Dadurch ist kein [Entfernen]-Link mehr für jede Person erforderlich. Ein einziger Link für die gesamte Tabelle reicht aus.

Das NetBeans-Projekt ist mit wenigen kleinen Unterschieden identisch mit dem vorherigen: dem Formular und seinem Modell. Das Formular [index.xhtml] sieht wie folgt aus:


<?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>
  • Zeile 13: Mit dem Attribut „selectionMode“ können Sie zwischen Einzel- und Mehrfachauswahl wählen. Hier haben wir uns dafür entschieden, nur eine einzelne Zeile auszuwählen,
  • Zeile 13: Das Attribut „rowkey“ bezeichnet ein Attribut der angezeigten Elemente, das deren eindeutige Auswahl ermöglicht. Hier haben wir die ID der ausgewählten Person gewählt,
  • Zeile 13: Das Attribut „selection“ bezeichnet das Modellattribut, das eine Referenz auf die ausgewählte Person erhält. Dank des vorangehenden Attributs „rowkey“ kann auf der Serverseite eine Referenz auf die ausgewählte Person berechnet werden. Die Einzelheiten der verwendeten Methode sind uns nicht bekannt. Wir können uns vorstellen, dass die Sammlung sequenziell durchlaufen wird, um das Element zu finden, das dem ausgewählten „rowkey“ entspricht. Das bedeutet: Wenn die Methode, die „rowkey“ mit der Auswahl verknüpft, komplexer ist, ist diese Methode nicht verwendbar,

Davon abgesehen entwickelt sich die Methode [Form].removePerson wie folgt:


...
 
@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
...
}
  • Zeile 9: Bei jedem POST wird die Referenz in Zeile 9 mit der Referenz der ausgewählten Person aus der Liste in Zeile 8 initialisiert,
  • in 18: Dies vereinfacht das Entfernen der Zeile. Die Suche, die wir im vorherigen Beispiel durchgeführt haben, erfolgte mithilfe des <dataTable>-Tags.

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

Dieses Projekt ähnelt dem vorherigen. Die Ansicht ist insbesondere identisch:

Image

Das NetBeans-Projekt ist identisch mit dem vorherigen, mit einigen geringfügigen Unterschieden, die wir uns ansehen werden. Das Formular [index.xhtml] ändert sich wie folgt:


...
  <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>
  • Zeile 6: Das Attribut „rowkey“ wurde entfernt, das Attribut „selection“ bleibt jedoch erhalten. Die Verbindung zwischen den Attributen „rowkey“ und „selection“ wird nun über eine Klasse hergestellt. Das Attribut „value“ in Zeile 5 „ “ enthält nun eine Instanz der PrimeFaces-Schnittstelle „SelectableDataModel<T>“. Die Methode [Form].getPeople des Modells wird wie folgt aktualisiert:

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

Dem Projekt wird somit ein neues Bean hinzugefügt:

Diese Bean sieht wie folgt aus:


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;
  }
}
  • Zeile 7: Die Klasse ist eine Instanz der Schnittstelle `SelectableDataModel`. Mindestens zwei Klassen implementieren diese Schnittstelle: `ListDataModel`, dessen Konstruktor eine Liste als Parameter akzeptiert, und `ArrayDataModel`, dessen Konstruktor ein Array als Parameter akzeptiert. Hier erweitert unser Bean die Klasse `ListDataModel`,
  • Zeilen 13–15: Der Konstruktor nimmt die Liste der von uns verwalteten Personen als Parameter entgegen. Dieser Parameter wird an die übergeordnete Klasse übergeben,
  • Zeile 18: Die Methode `getRowKey` übernimmt die Rolle des Attributs `rowkey`, das entfernt wurde. Sie muss das Objekt zurückgeben, das eine Person eindeutig identifiziert, in diesem Fall die ID der Person,
  • Zeile 23: Die Methode `getRowData` muss das ausgewählte Objekt anhand seines `rowkey` zurückgeben. Hier gibt sie also eine Person anhand ihrer ID zurück. Die so erhaltene Referenz wird dem Zielobjekt des Attributs `selection` im `dataTable`-Tag zugewiesen, hier dem Attribut `selection="#{form.personneChoisie}"`. Der Parameter der Methode ist der `rowkey` des vom Benutzer ausgewählten Objekts in Form einer Zeichenkette,
  • Zeilen 24–35: Geben Sie die Referenz auf die Person zurück, deren ID empfangen wurde. Diese Referenz wird dem Modell [Form].personneChoisie zugewiesen. Die Methode [retirerPersonne] bleibt daher unverändert:

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

Diese Technik wird verwendet, wenn die Beziehung zwischen den Attributen **rowkey und **selection keine einfache Eigenschaft-zu-Objekt-Beziehung ist (**rowkey** zu **selection**).

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

Dieses Projekt ähnelt dem vorherigen, außer dass sich die Methode zur Auswahl der zu entfernenden Person ändert:

Image

Oben sehen wir, dass das Objekt über ein Kontextmenü (Rechtsklick) ausgewählt wird. Es wird eine Bestätigung für den Löschvorgang angefordert:

 

Das Formular [index.xhtml] ändert sich wie folgt:


...
  <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>
  • Zeilen 9–11: Definieren Sie ein Kontextmenü für (for-Attribut) die dataTable in Zeile 21 (id-Attribut). Dieses Kontextmenü erscheint, wenn Sie mit der rechten Maustaste auf die Tabelle „people“ klicken,
  • Zeile 10: Unser Menü hat nur eine Option (menuItem-Tag). Wenn diese Option angeklickt wird, wird der JavaScript-Code im onclick-Attribut ausgeführt. Der JavaScript-Code [confirmation.show()] zeigt das Dialogfeld aus Zeile 14 (widgetVar-Attribut) an. Er lautet wie folgt:
  • Zeile 14: Das Attribut „message“ zeigt [3] an, das Attribut „header“ zeigt [1] an, das Attribut „severity“ zeigt das Symbol [2] an,
  • Zeile 16: zeigt [4] an. Nach dem Klicken wird die Person gelöscht (Aktionsattribut), anschließend wird das Dialogfeld geschlossen (oncomplete-Attribut). Das oncomplete-Attribut ist JavaScript-Code, der ausgeführt wird, sobald die serverseitige Aktion ausgeführt wurde,
  • Zeile 17: zeigt [5] an. Beim Klicken wird das Dialogfeld geschlossen und die Person nicht gelöscht.

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

Dieses Projekt zeigt, dass es möglich ist, nach der Ausführung eines AJAX-Aufrufs eine Antwort vom Server zu erhalten. Dazu verwenden wir das oncomplete-Attribut des AJAX-Aufrufs:

 

Das Formular [index.xhtml] ändert sich wie folgt:


...
  <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>
  • Zeile 7: Das Attribut „oncomplete“ ruft die JavaScript-Funktion in den Zeilen 13–18 auf,
  • Zeile 13: Die Methodensignatur muss diese sein. „args“ ist ein Dictionary, das das serverseitige Modell füllen kann,
  • Zeile 15: Wir prüfen, ob das „args“-Wörterbuch ein Attribut namens „msgError“ enthält. Ist dies der Fall, wird es angezeigt (Zeile 16).

Im Modell entwickelt sich die Methode [removePerson] wie folgt:


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);
    }
  }
  • Zeile 3: Erzeuge eine Zufallszahl 0 oder 1,
  • Zeilen 4–6: Wenn sie 0 ist, wird die vom Benutzer ausgewählte Person aus der Personenliste entfernt,
  • Zeile 9: andernfalls wird eine internationalisierte Fehlermeldung erstellt:

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.
  • Zeile 10: Eine komplexe Anweisung, die das Attribut „msgError“ zu dem zuvor erwähnten „args“-Wörterbuch hinzufügt, wobei der Wert von „msgError“ in Zeile 9 gebildet wurde. Dieses Attribut wird dann von der JavaScript-Methode in [index.xhtml] abgerufen:

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

5.18. Beispiel: mv-pf-15: die Symbolleiste

In diesem Projekt erstellen wir eine Symbolleiste:

Die Symbolleiste ist die Komponente, die im obigen Kasten zu sehen ist. Sie wird mit dem folgenden XHTML-Code erstellt [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>
  • Zeilen 15–22: die Symbolleiste,
  • Zeilen 16–18: Definieren die Gruppe von Komponenten links von der Symbolleiste,
  • Zeilen 19–21: dasselbe für die Komponenten auf der rechten Seite.

Die Komponenten links von der Symbolleiste sind wie folgt:


        <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>

Sie zeigen die folgende Ansicht an:

  • Zeile 2: zeigt [1] an,
  • Zeile 3: zeigt einen 30-Pixel-Abstand an [2],
  • Zeilen 4–6: zeigen eine Dropdown-Liste mit einer Liste von Personen an [3],
  • Zeile 7: zeigt ein Trennzeichen an [4],
  • Zeile 8: zeigt eine Schaltfläche [5] an, mit der die aus der Dropdown-Liste ausgewählte Person gelöscht werden kann. Die Schaltfläche verfügt über ein Symbol. Diese Symbole stammen aus jQuery UI. Eine Liste davon finden Sie unter der URL [http://jqueryui.com/themeroller/] [6]:
  • Um den Namen eines Symbols zu ermitteln, bewegen Sie einfach den Mauszeiger darüber. Dieser Name wird dann im Attribut „icon“ der Komponente <commandButton> verwendet, zum Beispiel icon="ui-icon-trash". Beachten Sie, dass oben der Name .ui-icon-trash angegeben ist und dass der führende Punkt in diesem Namen im Attribut „icon“ entfernt wird,
  • Zeile 9: Erstellt einen Tooltip für die Schaltfläche (für das Attribut). Wenn Sie mit der Maus über die Schaltfläche fahren, erscheint die Tooltip-Meldung [7].

Die mit diesen Komponenten verknüpfte Vorlage lautet wie folgt:


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
  ...
}

Die Komponenten auf der rechten Seite der Symbolleiste sind wie folgt:


<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>

Sie zeigen die folgende Ansicht an:

 
  • Zeilen 6–9: eine Menüschaltfläche. Sie enthält Menüoptionen,
  • Zeile 7: die Option, das Formular auf Französisch umzustellen,
  • Zeile 8: die Option, es auf Englisch umzustellen.

5.19. Fazit

Wir wissen nun genug, um unsere Beispielanwendung auf PrimeFaces zu portieren. Wir haben nur etwa fünfzehn Komponenten behandelt, während die Bibliothek über 100 enthält. Leser werden dazu ermutigt, direkt auf der PrimeFaces-Website nach fehlenden Komponenten zu suchen.

5.20. Testen mit Eclipse

Die Maven-Projekte sind auf der Beispiel-Website [1] verfügbar:

Sobald sie in Eclipse importiert wurden, können sie ausgeführt werden [2]. Wählen Sie unter [3] „Tomcat“ aus. Sie werden dann im internen Browser von Eclipse angezeigt [3].