Skip to content

5. Wprowadzenie do biblioteki komponentów PrimeFaces

5.1. Rola Primefaces w aplikacji JSF

Wróćmy do architektury aplikacji JSF, którą omówiliśmy na początku niniejszego dokumentu:

Strony JSF zostały zbudowane przy użyciu trzech bibliotek tagów:

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">
  • wiersz 2: tagi <h:x> z przestrzeni nazw [http://java.sun.com/jsf/html], które odpowiadają tagom HTML,
  • wiersz 3: tagi <f:y> z przestrzeni nazw [http://java.sun.com/jsf/core], które odpowiadają tagom JSF,
  • wiersz 4: tagi <ui:z> z przestrzeni nazw [http://java.sun.com/jsf/facelets], które odpowiadają tagom faceletów.

Aby utworzyć strony JSF, dodamy czwartą bibliotekę znaczników – znaczniki komponentów Primefaces.

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml"
      ...
xmlns:p="http://primefaces.org/ui">
  • wiersz 3: tagi <p:z> z przestrzeni nazw [http://primefaces.org/ui] odpowiadają komponentom Primefaces.

To jedyna zmiana, która zostanie wprowadzona. Pojawi się ona zatem w widokach. Obsługa zdarzeń i modele pozostają takie same jak w przypadku JSF. Jest to ważna kwestia, którą należy zrozumieć.

Wykorzystanie komponentów Primefaces pozwala tworzyć interfejsy internetowe bardziej przyjazne dla użytkownika dzięki licznym komponentom tej biblioteki oraz bardziej płynne dzięki technologii AJAX, z której korzysta ona natywnie. Mówimy wówczas o interfejsach rozbudowanych lub RIA (Rich Internet Application).

Poprzednia architektura JSF zostanie zastąpiona następującą architekturą PF (PrimeFaces):

5.2. Zalety Primefaces

Strona Primefaces [http://www.primefaces.org/showcase/ui/home.jsf] zawiera listę komponentów, które można wykorzystać na stronie PF:

W kolejnych przykładach wykorzystamy dwie pierwsze funkcje Primefaces:

  • niektóre spośród około stu oferowanych komponentów,
  • ich natywne zachowanie AJAX.

Wśród oferowanych komponentów znajdują się:

W naszych przykładach wykorzystamy tylko około piętnastu z nich, ale to wystarczy, aby zrozumieć zasady tworzenia strony w Primefaces.

5.3. Nauka korzystania z Primefaces

Primefaces udostępnia przykłady wykorzystania każdego ze swoich komponentów. Wystarczy kliknąć odpowiedni link. Przyjrzyjmy się przykładowi:

  • w [1], przykład dla komponentu [Spinner],
  • w [2] – okno dialogowe wyświetlane po kliknięciu przycisku [Submit].

Mamy tu trzy nowości:

  • komponent [Spinner], który domyślnie nie występuje w JSF,
  • to samo dotyczy okna dialogowego,
  • wreszcie komponent POST wywołany przez [Submit] działa w technologii AJAX. Jeśli przyjrzymy się uważnie przeglądarce podczas działania POST, nie widać ikony klepsydry. Strona nie jest odświeżana. Jest po prostu modyfikowana: na stronie pojawia się nowy element, w tym przypadku okno dialogowe.

Zobaczmy, jak to wszystko się odbywa. Kod XHTML z przykładu wygląda następująco:


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

Po pierwsze, zauważmy, że występują tu klasyczne tagi JSF: <h:form> w wierszu 1, <h:panelGrid> w wierszu 3, <h:outputLabel> w wierszu 4. Niektóre znaczniki JSF są przejmowane przez PF i wzbogacane: <p:commandButton> wiersz 21. Następnie występują tagi formatujące PF: <p:panel> wiersz 2, <p:outputPanel> wiersz 13, <p:dialog> wiersz 23. Na koniec mamy tagi do wprowadzania danych: <p:spinner> wiersz 5.

Przeanalizujmy ten kod w odniesieniu do widoku:

  • w [1] – komponent uzyskany za pomocą tagu <p:panel> z linii 2,
  • w [2] – pole wprowadzania danych utworzone przez połączenie tagów <p:outputLabel> i <p:spinner>, wiersze 6 i 7,
  • w [3] – przycisk z POST utworzony za pomocą tagu <p:commandButton> z wiersza 21,
  • w [4] – okno dialogowe z wierszy 23–25,
  • w [5] – niewidoczny kontener dla dwóch komponentów. Jest on tworzony przez tag <p:outputPanel> w wierszu 13.

Przeanalizujmy poniższy kod, który realizuje akcję 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>

Ten kod generuje następujący widok:

  • wiersz 1: wyświetla tekst [1]. Jest to jednocześnie etykieta dla komponentu id=ajaxspinner (atrybut „for”). Komponent ten jest tym z wiersza 3 (atrybut „id”),
  • wiersze 3–5: wyświetlają komponent [2]. Komponent ten jest komponentem wprowadzania/wyświetlania powiązanym z szablonem #{spinnerController.number5} (atrybut „value”),
  • wiersz 6: wyświetla komponent [3]. Ten komponent jest komponentem wyświetlającym powiązanym z szablonem #{spinnerController.number5} (atrybut „value”),
  • wiersz 4: tag <p:ajax> dodaje zachowanie AJAX do komponentu spinner. Za każdym razem, gdy ten komponent zmienia wartość, do modelu #{spinnerController.number5}. Następnie następuje aktualizacja strony (atrybut update). Wartością tego atrybutu jest identyfikator komponentu strony, w tym przypadku ten z wiersza 6. Komponent docelowy atrybutu update jest wówczas aktualizowany zgodnie z modelem. Jest to ponownie #{spinnerController.number5}, a więc wartość spinner. W ten sposób pole [3] śledzi wpisy z pola [2].

Mamy tu do czynienia z zachowaniem AJAX, skrótem oznaczającym Asynchronous JavaScript And XML. Ogólnie rzecz biorąc, zachowanie AJAX wygląda następująco:

  • przeglądarka wyświetla stronę HTML zawierającą kod JavaScript (J z AJAX). Elementy strony tworzą obiekt JavaScript zwany DOM (Document Object Model),
  • serwer hostuje aplikację internetową, która wygenerowała tę stronę,
  • w [1] na stronie ma miejsce zdarzenie. Na przykład inkrementacja spinner. Zdarzenie to jest obsługiwane przez JavaScript,
  • W [2] skrypt JavaScript generuje POST dla aplikacji internetowej. Odbywa się to asynchronicznie (litera „A” w AJAX). Użytkownik może nadal pracować ze stroną. Nie jest ona zawieszona, ale w razie potrzeby można ją zawiesić. POST aktualizuje szablon strony na podstawie przesłanych wartości, w tym przypadku szablon #{spinnerController.number5},
  • na [3], a aplikacja internetowa zwraca do JavaScriptu odpowiedź XML (X z AJAX) lub JSON (JavaScript w notacji obiektowej),
  • w przypadku [4] kod JavaScript wykorzystuje tę odpowiedź do aktualizacji konkretnego obszaru w DOM, w tym przypadku obszaru w id=ajaxspinnervalue.

W przypadku korzystania z JSF i Primefaces kod JavaScript jest generowany przez Primefaces. Biblioteka ta opiera się na bibliotece JavaScript JQuery. Podobnie komponenty Primefaces opierają się na komponentach z biblioteki JQuery i UI (interfejs użytkownika). Zatem JQuery stanowi podstawę Primefaces.

Wróćmy do naszego przykładu i przedstawmy teraz komponent POST przycisku [Submit]:

Kod powiązany z POST jest następujący:


<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>
  • wiersz 1: POST jest wywoływany przez przycisk w wierszu 1. W Primefaces tagi wywołujące POST domyślnie robią to w formie wywołania AJAX. Dlatego tagi te posiadają atrybut „update”, który wskazuje obszar do aktualizacji po otrzymaniu odpowiedzi z serwera. W tym przypadku aktualizowanym obszarem jest element panelGrid w wierszu 4. Po powrocie elementu POST obszar ten zostanie zaktualizowany o wartości przesłane do modelu. Znajdują się one jednak wewnątrz okna dialogowego, które domyślnie jest niewidoczne. Wyświetla je atrybut „oncomplete” w wierszu 1. Zdarzenie to ma miejsce po zakończeniu przetwarzania POST. Wartością tego atrybutu jest kod JavaScript. W tym przypadku wyświetlane jest okno dialogowe o identyfikatorze id=dialog, czyli to z wiersza 3 (atrybut widgetVar),
  • wiersz 3: widzimy różne atrybuty okna dialogowego. Trzeba poeksperymentować, aby sprawdzić, do czego służą.

Wspomnieliśmy o szablonie, ale jeszcze go nie przedstawiliśmy. Oto on:

public class SpinnerController {

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

     // metody pobierające i ustawiające
...
}

Ogólnie rzecz biorąc, można postępować w następujący sposób:

  • znaleźć komponent Primefaces, którego chcemy użyć,
  • zapoznać się z jego przykładem. Przykłady Primefaces są dobrze przygotowane i łatwe do zrozumienia.

5.4. Pierwszy projekt Primefaces: mv-pf-01

Stwórzmy projekt internetowy Maven w NetBeans:

  • [1, 2, 3]: tworzymy projekt Maven typu [Web Application],
  • [4]: serwerem będzie Tomcat,
  • w [5], wygenerowanym projekcie,
  • w [6] usuwamy z niego plik [index.jsp] oraz pakiet Java,
  • w [7, 8]: w właściwościach projektu dodajemy obsługę Java Server Faces,
  • do [9], a w zakładce [Components] wybieramy bibliotekę komponentów PrimeFaces. NetBeans oferuje obsługę innych bibliotek komponentów: ICEFaces i RichFaces.
  • W pliku [10] znajduje się wygenerowany projekt. W pliku [11] należy zwrócić uwagę na zależność od Primefaces.

Mówiąc wprost, projekt Primefaces to klasyczny projekt JSF, do którego dodano zależność od Primefaces. Nic więcej.

Mając to na uwadze, modyfikujemy plik [pom.xml], aby mógł współpracować z najnowszymi wersjami bibliotek:


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

W wierszach 26–30 należy zwrócić uwagę na repozytorium Maven dla Primefaces. Po wprowadzeniu tych zmian kompilujemy projekt, aby rozpocząć pobieranie zależności. W ten sposób otrzymujemy projekt [12].

Teraz spróbujmy odtworzyć przykład, który analizowaliśmy. Strona [index.html] wygląda następująco:


<?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>
    <!-- formularz -->
    <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()" />
      <!-- okno dialogowe -->
      <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>

Nie zapominamy o wierszu 5, który deklaruje przestrzeń nazw biblioteki tagów Primefaces. Do projektu dodajemy bean, który służy jako szablon dla strony:

  

Bean ma następującą postać:


package beans;

import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;

@ManagedBean
@RequestScoped
public class SpinnerController {

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

  // metody pobierające i ustawiające
  ...
}

Klasa jest beanem (wiersz 6) o zasięgu żądania (wiersz 7). Ponieważ nie podano nazwy, bean nosi nazwę klasy z pierwszą literą pisanej małą literą: spinnerController.

Po uruchomieniu projektu otrzymujemy następujący wynik:

 

W ten sposób pokazaliśmy, jak przetestować przykład zaczerpnięty ze strony Primefaces. Wszystkie przykłady można przetestować w ten sposób.

W dalszej części skupimy się wyłącznie na niektórych komponentach Primefaces. Najpierw powrócimy do przykładów omówionych z wykorzystaniem JSF i zastąpimy niektóre tagi JSF tagami Primefaces. Wygląd stron ulegnie niewielkiej zmianie – będą one zachowywać się zgodnie z AJAX, ale powiązane z nimi komponenty typu bean nie będą wymagały modyfikacji. W każdym z poniższych przykładów ograniczamy się do przedstawienia kodu stron oraz powiązanych zrzutów ekranu. Zachęcamy czytelnika do przetestowania przykładów w celu wykrycia różnic między stronami a. b. i c.

5.5. Przykład mv-pf-02: menedżer zdarzeń – internacjonalizacja – nawigacja między stronami

Projekt ten stanowi adaptację projektu JSF [mv-jsf2-02] (punkt 2.4, strona 41):

Projekt NetBeans wygląda następująco:

Strona [index.html] wygląda następująco:


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

W wierszach 15, 16 i 19 tagi <h:commandLink> zostały zastąpione tagami <p:commandLink>. Ten tag domyślnie zachowuje się jak AJAX, co można wyłączyć, ustawiając atrybut ajax="false". W tym przypadku tagi <p:commandLink> zachowują się jak tagi <h:commandLink>: po kliknięciu tych linków nastąpi odświeżenie strony.

5.6. Przykład mv-pf-03: układ strony przy użyciu faceletów

Ten projekt przedstawia tworzenie stron XHTML przy użyciu szablonów facelets z przykładu [mv-jsf2-09] (punkt 2.11):

 

Projekt NetBeans wygląda następująco:

  • w [1], pliki konfiguracyjne projektu JSF,
  • w [2] – strony XHTML,
  • na [3] – komponent obsługujący zmianę języków,
  • w [4] – pliki komunikatów,
  • na [5], zależności.

Strony projektu są oparte na szablonie strony [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>
  • wiersz 9: tag <f:view> otacza całą stronę, aby wykorzystać możliwości internacjonalizacji, jakie zapewnia,
  • wiersz 15: formularz o identyfikatorze „form”. Formularz ten stanowi treść strony. W tej treści znajduje się tylko jedna część dynamiczna, obejmująca wiersze 28–30. To właśnie tam zostanie wstawiona zmienna część strony:
  • obszar zaznaczony ramką powyżej będzie aktualizowany za pomocą wywołań AJAX. Aby go zidentyfikować, umieściliśmy go w kontenerze Primefaces wygenerowanym przez tag <p:outputPanel> (wiersz 27). Kontener ten został nazwany „contenu” (atrybut id). Ponieważ znajduje się on w formularzu, który sam w sobie jest kontenerem o nazwie „formulaire”, pełna nazwa dynamicznego obszaru brzmi: :formulaire:contenu. Pierwszy znak „:” oznacza, że zaczynamy od korzenia dokumentu, następnie przechodzimy do kontenera o nazwie „formulaire”, a potem do kontenera o nazwie „contenu”. Trudność związaną z AJAX polega na prawidłowym nazwie pol, które mają zostać zaktualizowane poprzez wywołanie AJAX. Najprościej jest przejrzeć kod źródłowy otrzymanej strony HTML:

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

Powyżej widać, że tag <h:outputPanel> wygenerował tag HTML <span>. W tym przykładzie nazwa względna formularz:treść (bez początkowego znaku :) oraz nazwa pełna :formularz:treść (z początkowym znakiem :) odnoszą się do tego samego obiektu.

Należy pamiętać, że wywołania AJAX (<p:commandButton>, <p:commandLink>), aktualizujące obszar dynamiczny, będą miały atrybut update=":formularz:treść".

Strona [index.xhtml] jest jedyną stroną wyświetlaną przez projekt:


<?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>
  • w wierszu 8 szablonem dla [index.xhtml] jest strona [layout.xhtml], którą właśnie przedstawiono,
  • w wierszu 9 znajduje się obszar identyfikatora treści, który jest aktualizowany przez [index.html]. W tym obszarze znajdują się dwa fragmenty:
    • fragment [page1.xhtml], wiersz 11;
    • fragment [page2.xhtml] w wierszu 14.

Te dwa fragmenty wykluczają się wzajemnie.

  • wiersz 10: fragment [page1.xhtml] jest wyświetlany, jeśli zapytanie ma atrybut page1 ustawiony na true lub jeśli atrybut page2 nie istnieje. Dzieje się tak w przypadku pierwszego zapytania, w którym żaden z tych atrybutów nie będzie obecny w zapytaniu. W takim przypadku wyświetlany będzie fragment [page1.xhtml],
  • wiersz 11, a fragment [page2.xhtml] jest wyświetlany, jeśli zapytanie ma atrybut „page2” ustawiony na „true”

Fragment [page1.xhtml] ma następującą treść:


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

i wyświetla następującą treść:

  • wiersze 11 i 12 – dwa linki służące do zmiany języka. Oba te linki powodują wywołania funkcji AJAX (ajax=true). Jest to ustawienie domyślne. Nie ma więc potrzeby dodawania atrybutu ajax=true. W dalszej części już tego nie będziemy robić. Należy zauważyć, że te dwa linki aktualizują obszar :formularz:treść (atrybut update), czyli ten, który został zaznaczony ramką powyżej,
  • wiersz 15: link nawigacyjny AJAX, który również aktualizuje obszar :formularz:treść,
  • wiersz 16: używamy tagu <h:setPropertyActionListener>, aby umieścić w zapytaniu atrybut page2 o wartości true. Spowoduje to wyświetlenie fragmentu [page2.xhtml] (wiersz 6 poniżej) na stronie [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>

Fragment [page2.xhtml] działa analogicznie:

Kod [page2.xhtml] jest następujący:


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

Z tego przykładu wyciągnijmy następujące wnioski na przyszłość:

  • będziemy używać szablonu [layout.xhtml] jako szablonu stron,
  • obszar dynamiczny będzie oznaczony identyfikatorem:formularz:treść i będzie aktualizowany poprzez wywołania AJAX.

5.7. Przykład mv-pf-04: formularz wprowadzania danych

Projekt ten stanowi adaptację projektu JSF2 [mv-jsf2-03] (patrz punkt 2.5):

Projekt NetBeans ma następującą nazwę:

Powyżej, w pliku [1], znajdują się strony XHTML projektu. Układ strony zapewnia omówiony wcześniej szablon [layout.xhtml]. Strona [index.xhtml] jest jedyną stroną projektu. Wyświetla się ona w obszarze :formularz:treść. Jej kod wygląda następująco:


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

Jej zadaniem jest jedynie wyświetlenie fragmentu [page1.xhtml]. Jest on odpowiednikiem formularza omówionego w przykładzie [mv-jsf2-03]. Przypomnijmy, że jego celem było przedstawienie tagów wprowadzania danych JSF. Tagi te zostały tutaj zastąpione tagami Primefaces.

PanelGrid

Aby sformatować elementy z [page1.xhtml], używamy tagu <p:panelGrid>. Na przykład w przypadku dwóch linków do języków:


<!-- języki -->
    <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>

Daje to następujący efekt:

 

Inna forma tagu <p:panelGrid> wygląda następująco:


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

Wiersze i kolumny tabeli są oznaczone tagami <p:row> i <p:column>.

Wiersze 3–12 definiują nagłówek tabeli:

 

Wiersze 14–25 definiują wiersz tabeli:

 

Wiersze 27–35 definiują stopkę tabeli:

 

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>

W wierszu 7 atrybut feedback=true pozwala uzyskać informację zwrotną na temat jakości hasła [1].

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>

Wiersz 7: tag <p:editor> wyświetla edytor sformatowany, który pozwala formatować tekst (czcionka, rozmiar, kolor, wyrównanie itp.). Na serwer wysyłany jest kod HTML wprowadzonego tekstu [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>
 

W wierszu 14 należy zauważyć, że link [Raz] aktualizuje pole :formularz:selectManyMenu za pomocą AJAX, co odpowiada komponentowi z wiersza 6. Należy jednak pamiętać, że podczas operacji POST AJAX przesyłane są wszystkie wartości formularza. Zatem aktualizowany jest cały szablon. W przypadku tego szablonu aktualizowane jest jednak tylko pole :formularz: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. Przykład: mv-pf-05: listy dynamiczne

Ten projekt stanowi adaptację projektu JSF2 [mv-jsf2-04] (patrz punkt 2.6):

Image

Projekt ten nie wprowadza żadnych nowych tagów Primefaces w porównaniu z poprzednim projektem. Dlatego nie będziemy go omawiać. Znajduje się on na liście przykładów udostępnionych czytelnikowi na stronie internetowej dokumentu.

5.9. Przykład: mv-pf-06: nawigacja – sesja – obsługa wyjątków

Ten projekt jest adaptacją projektu JSF2 [mv-jsf2-05] (patrz punkt 2.7):

Również w tym przykładzie nie wprowadzono żadnych nowych tagów Primefaces. Omówimy jedynie tabelę linków zamieszczoną w ramce powyżej:


<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>
  • wszystkie linki mają atrybut ajax=false. Strona ładuje się więc w normalny sposób,
  • zwróćmy uwagę na wiersze 2 i 4, pokazujące sposób wykonania przekierowania.

5.10. Przykład: mv-pf-07: walidacja i konwersja danych wprowadzonych przez użytkownika

Ten projekt jest adaptacją projektu JSF2 [mv-jsf2-06] (patrz punkt 2.8):

Image

Aplikacja wprowadza dwa nowe tagi, w tym tag <p:messages>:


<p:messages globalOnly="true"/>

Image

oraz tag <p:message>:


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

W porównaniu z tagiem <h:message> o wartości JSF, tag <p:message> o wartości PF powoduje następujące zmiany:

  • wygląd komunikatu o błędzie jest inny ([1]),
  • pole, w którym wprowadzono błędne dane, jest otoczone czerwoną ramką w [2].

5.11. Przykład: mv-pf-08: zdarzenia związane ze zmianą stanu komponentów

Niniejszy projekt stanowi przeniesienie projektu JSF2 [mv-jsf2-07] (patrz punkt 2.9):

Image

Projekt JSF wprowadzał pojęcie listeners. Obsługa listener w Primefaces odbywała się w inny sposób.

W przypadku JSF:

1
2
3
4
5
6
7
         <!-- wiersz 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}"/>

W przypadku 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>
  • wiersz 2: tag <h:selectOneMenu> bez atrybutu valueChangeListener,
  • wiersz 4: tag <p:ajax> dodaje zachowanie AJAX do swojego tagu nadrzędnego <h:selectOneMenu>. Domyślnie reaguje on na zdarzenie „zmiana wartości” listy combo1. W momencie wystąpienia tego zdarzenia wartości formularza, do którego należy, zostaną przesłane na serwer za pomocą wywołania AJAX. Model zostaje zatem zaktualizowany. Ten nowy model wykorzystujemy do aktualizacji listy rozwijanej oznaczonej jako combo2 (wiersz 10). Warto zauważyć, że w wierszu 4 wywołanie AJAX nie uruchamia żadnej metody modelu. Nie jest to tutaj konieczne. Chcemy po prostu zmienić model na POST na podstawie wprowadzonych wartości.

5.12. Przykład: mv-pf-09: wspomagane wprowadzanie danych

Ten projekt zawiera tagi wprowadzania danych specyficzne dla Primefaces, które ułatwiają wprowadzanie niektórych typów danych:

5.12.1. Projekt NetBeans

Projekt NetBeans wygląda następująco:

Znaczenie tego projektu polega na:

  • wyświetlanej przez niego jedynej stronie [index.html],
  • szablonie [Form.java] tej strony.

5.12.2. Szablon

Formularz zawiera cztery pola wprowadzania danych powiązane z następującym szablonem:


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) {
    ...
  }
   // metody pobierające i ustawiające
...
}

Te cztery wpisy są powiązane z polami w wierszach 14–17.

5.12.3. Formularz

Formularz wygląda następująco:


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

        <!-- kalendarz -->
              ...

        <!-- suwak -->
              ...

        <!-- koło obrotowe -->
              ...

        <!-- autouzupełnianie -->
              ...

      </p:panelGrid>
    </ui:define>
  </ui:composition>
</html>

Przyjrzyjmy się czterem polom do wypełnienia.

5.12.4. Kalendarz

Tag <p:calendar> umożliwia wybranie daty z kalendarza. Tag ten obsługuje różne atrybuty.


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

W wierszu 2 określa się, że data ma być wyświetlana w formacie „dd/mm/rrrr”, a strefą czasową jest strefa paryska. Po umieszczeniu kursora w polu wprowadzania danych wyświetla się kalendarz:

 

5.12.5. Suwak

Tag <p:slider> umożliwia wprowadzenie liczby całkowitej poprzez przesunięcie suwaka wzdłuż paska:

 

Kod znacznika wygląda następująco:


        <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}"/>
  • wiersz 3: mamy tu klasyczny tag <p:inputText>, który umożliwia wprowadzenie liczby całkowitej. Liczbę tę można również wprowadzić za pomocą suwaka,
  • wiersz 4: tag <p:slider> jest powiązany z tagiem wprowadzania danych <p:inputText> (atrybut „for”). Ustawia się dla niego wartość minimalną i maksymalną.

5.12.6. Spinner

Mieliśmy już okazję przedstawić ten komponent:


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

Wiersz 3: spinner umożliwia wprowadzenie liczby całkowitej z przedziału od 1 do 12. Liczbę można wpisać bezpośrednio w polu wprowadzania spinnera lub użyć strzałek, aby zwiększyć lub zmniejszyć wprowadzoną wartość.

 

5.12.7. Wprowadzanie wspomagane

Wspomagane wprowadzanie danych polega na wpisaniu pierwszych znaków. Następnie w rozwijanej liście pojawiają się propozycje. Można wybrać jedną z nich. Z tego komponentu korzysta się zamiast list rozwijanych, gdy ich zawartość jest zbyt obszerna. Załóżmy, że chcemy udostępnić listę rozwijaną zawierającą miasta we Francji. Jest to kilka tysięcy miast. Jeśli pozwolimy użytkownikowi wpisać trzy pierwsze znaki nazwy miasta, możemy następnie zaproponować mu skróconą listę miast zaczynających się od tych znaków.

 

Kod tego komponentu wygląda następująco:


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

Tag <p:autoComplete> w wierszu 2 umożliwia funkcję podpowiedzi podczas wpisywania. Parametr, który nas tutaj interesuje, to atrybut completeMethod, którego wartość stanowi nazwę metody modelu odpowiedzialnej za generowanie propozycji odpowiadających znakom wpisanym przez użytkownika. Metoda ta ma następującą postać:


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

  • wiersz 1: metoda otrzymuje jako parametr ciąg znaków wpisanych przez użytkownika w polu wprowadzania danych. Zwraca listę propozycji,
  • wiersze 4–6: tworzy się listę 10 propozycji, która zawiera znaki otrzymane jako parametry oraz dodaje do nich cyfrę od 0 do 9.

5.12.8. Tag <p:growl>

Tag <p:growl> stanowi alternatywę dla tagu <p:messages>, który wyświetla komunikaty o błędach formularza.


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

W powyższym przykładzie atrybut id nie jest używany. Atrybut autoUpdate=true oznacza, że lista komunikatów o błędach musi być odświeżana przy każdym POST formularza.

Załóżmy, że zatwierdzamy następujący formularz [1]:

  • po wysłaniu w [2] tag <p:growl> wyświetla komunikaty o błędach związane z nieprawidłowymi danymi wprowadzonymi.

5.13. Przykład: mv-pf-10: dataTable - 1

W tym projekcie przedstawiono tag <p:dataTable>, który służy do wyświetlania list danych

Image

5.13.1. Projekt NetBeans

Projekt NetBeans wygląda następująco:

Znaczenie tego projektu polega na:

  • wyświetlanej przez niego jedynej stronie o nazwie [index.html],
  • szablonie tej strony o nazwie [Form.java] oraz bean o nazwie [Personne].

5.13.2. Plik komunikatów

Plik [messages_fr.properties] ma następującą treść:


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

Bean o nazwie [Personne] reprezentuje osobę:


package forms;

import java.io.Serializable;

public class Personne implements Serializable{
  // dane
  private int id;
  private String nom;
  private String prénom;
  
  // konstruktorzy
  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);
  }
  
  // metody pobierające i ustawiające
...
}

Szablon strony [index.xhtml] to następująca klasa [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;

  // konstruktor
  public Form() {
    // inicjalizacja listy osób
    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() {
...
  }
  
  // metody pobierające i ustawiające
...  
}
  • wiersze 9–10: bean ma zasięg sesji,
  • wiersze 18–24: konstruktor tworzy listę trzech osób, która będzie więc istniała przez cały czas trwania żądań,
  • wiersz 15: numer osoby, którą należy usunąć z listy,
  • wiersze 26–28: metoda usuwania.

5.13.4. Formularz

Formularz ma następujący numer: [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>

Powoduje to wyświetlenie następującego widoku (ramka poniżej):

  • wiersz 12: generuje tabelę zaznaczoną ramką powyżej. Atrybut „value” określa kolekcję wyświetlaną w tabeli, w tym przypadku listę osób z modelu. Atrybut emptyMessage jest opcjonalny. Określa on komunikat, który ma zostać wyświetlony, gdy lista jest pusta. Domyślnie jest to „no records found”. W tym przypadku będzie to:
 
  • wiersze 13–15: generują nagłówek [1],
  • wiersze 16–21: generują kolumnę [2],
  • wiersze 22–27: generują kolumnę [3],
  • wiersze 28–33: generują kolumnę [4],
  • wiersze 34–41: generują kolumnę [5].

Link [Retirer] pozwala usunąć osobę z listy. Wiersz [38] – to metoda [Form].retirerPersonne wykonuje tę czynność. Metoda ta wymaga podania numeru osoby, którą należy usunąć. Numer ten jest podany w wierszu 39. W wierszu 38 użyto atrybutu „action”. W innych przypadkach używano atrybutu actionListener. Nie jestem pewien, czy dobrze rozumiem różnicę funkcjonalną między tymi dwoma atrybutami. W praktyce zauważamy jednak, że atrybuty ustawione za pomocą tagów <setPropertyActionListener> są ustalane przed wykonaniem metody wskazanej przez atrybut „action”, podczas gdy w przypadku atrybutu actionListener tak nie jest. Mówiąc prościej, gdy tylko mamy parametry do przesłania do wywoływanej akcji, należy użyć atrybutu action.

Metoda usunięcia osoby wygląda następująco:


...
@ManagedBean
@SessionScoped
public class Form implements Serializable{

  // wzór
  private List<Personne> personnes;
  private int personneId;

  public void retirerPersonne() {
    // szukamy wybranej osoby
    int i = 0;
    for (Personne personne : personnes) {
      // aktualna osoba = wybrana osoba?
      if (personne.getId() == personneId) {
        // usuwamy aktualną osobę z listy
        personnes.remove(i);
        // zakończono
        break;
      } else {
        // następna osoba
        i++;
      }
    }
  }
...  
}

5.14. Przykład: mv-pf-11: dataTable - 2

W tym projekcie przedstawiono tabelę zawierającą listę danych, z której można wybrać wiersz:

Zaznaczenie wiersza w tabeli powoduje, że w momencie wykonania POST do modelu wysyłane są informacje o zaznaczonym wierszu. Dzięki temu nie jest już potrzebne osobne połączenie [Retirer] dla każdej osoby. Wystarczy jedno dla całej tabeli.

Projekt NetBeans jest identyczny z poprzednim, z wyjątkiem kilku szczegółów: formularza i jego szablonu. Formularz [index.xhtml] wygląda następująco:


<?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>
  • wiersz 13: atrybut selectionMode pozwala wybrać tryb selekcji single lub multiple. W tym przypadku zdecydowaliśmy się zaznaczyć tylko jeden wiersz,
  • wiersz 13: atrybut rowkey określa atrybut wyświetlanych elementów, który pozwala na ich jednoznaczny wybór. W tym przypadku wybraliśmy identyfikator wybranej osoby,
  • wiersz 13: atrybut selection określa atrybut modelu, który otrzyma odwołanie do wybranej osoby. Dzięki poprzedniemu atrybutowi rowkey po stronie serwera będzie można obliczyć odwołanie do wybranej osoby. Nie znamy szczegółów stosowanej metody. Można sobie wyobrazić, że kolekcja jest przeglądana sekwencyjnie w poszukiwaniu elementu odpowiadającego wybranemu rowkey. Oznacza to, że jeśli metoda łącząca rowkey z selection jest bardziej złożona, to nie można jej zastosować,

Mając to na uwadze, metoda [Form].retirerPersonne zmienia się w następujący sposób:


...

@ManagedBean
@SessionScoped
public class Form implements Serializable {

  // szablon
  private List<Personne> personnes;
  private Personne personneChoisie;

  // producent
  public Form() {
  ...
  }

  public void retirerPersonne() {
    // usuwamy wybraną osobę
    personnes.remove(personneChoisie);
  }

  // metody pobierające i ustawiające
...
}
  • wiersz 9: przy każdym wystąpieniu POST odwołanie w wierszu 9 jest inicjowane odwołaniem do wybranej osoby z listy w wierszu 8,
  • w 18: ułatwia to usunięcie osoby. Wyszukiwanie, które przeprowadziliśmy w poprzednim przykładzie, zostało wykonane za pomocą znacznika <dataTable>.

5.15. Przykład: mv-pf-12: dataTable - 3

Ten projekt jest analogiczny do poprzedniego. Widok jest w szczególności identyczny:

Image

Projekt NetBeans jest identyczny z poprzednim, z wyjątkiem kilku szczegółów, które omówimy poniżej. Formularz [index.xhtml] zmienia się w następujący sposób:


...
  <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>
  • W wierszu 6 atrybut rowkey zniknął, atrybut selection pozostał. Powiązanie między atrybutami rowkey i selection odbywa się teraz za pośrednictwem klasy. Atrybut „value” w wierszu 5 ma teraz jako wartość instancję interfejsu Primefaces SelectableDataModel<T>. Metoda [Form].getPersonnes w modelu zmienia się w następujący sposób:

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

W ten sposób do projektu dodano nową fasolę:

Ten bean ma następującą postać:


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

  // konstruktory
  public DataTableModel() {
  }

  public DataTableModel(List<Personne> personnes) {
    super(personnes);
  }

  @Override
  public Object getRowKey(Personne personne) {
    return personne.getId();
  }

  @Override
  public Personne getRowData(String rowKey) {
    // lista osób
    List<Personne> personnes = (List<Personne>) getWrappedData();
    // klucz jest liczbą całkowitą 
    int key = Integer.parseInt(rowKey);
    // wyszukiwanie wybranej osoby
    for (Personne personne : personnes) {
      if (personne.getId() == key) {
        return personne;
      }
    }
    // nie znaleziono żadnych wyników
    return null;
  }
}
  • wiersz 7: klasa jest instancją interfejsu SelectableDataModel. Co najmniej dwie klasy implementują ten interfejs: ListDataModel, której konstruktor przyjmuje listę jako parametr, oraz ArrayDataModel, której konstruktor przyjmuje tablicę jako parametr. W tym przypadku nasz bean dziedziczy po klasie ListDataModel,
  • wiersze 13–15: konstruktor przyjmuje jako parametr listę osób, którymi zarządzamy. Parametr ten jest przekazywany do klasy nadrzędnej,
  • wiersz 18: metoda getRowKey pełni rolę usuniętego atrybutu rowkey. Musi ona zwracać obiekt, który pozwala jednoznacznie zidentyfikować osobę, w tym przypadku identyfikator osoby,
  • wiersz 23: metoda getRowData musi zwrócić wybrany obiekt na podstawie jego rowkey. W tym przypadku oznacza to zwrócenie osoby na podstawie jej identyfikatora. Uzyskany w ten sposób odnośnik zostanie przypisany do obiektu docelowego atrybutu selection w tagu dataTable, w tym przypadku atrybutu selection="#{form.personneChoisie}". Parametrem metody jest rowkey obiektu wybranego przez użytkownika, w postaci ciągu znaków,
  • wiersze 24–35: zwracają odwołanie do osoby, której identyfikator został otrzymany. Odwołanie to zostanie przypisane do modelu [Form].personneChoisie. Metoda [retirerPersonne] pozostaje zatem bez zmian:

  public void retirerPersonne() {
    // usuwamy wybraną osobę
    personnes.remove(personneChoisie);
  }

Jest to technika, którą należy zastosować, gdy powiązanie między atrybutami rowkey i selection nie jest zwykłym powiązaniem między właściwością (rowkey) a obiektem (selection).

5.16. Przykład: mv-pf-13: dataTable - 4

Ten projekt jest analogiczny do poprzedniego, z tą różnicą, że zmienia się sposób wyboru osoby, którą należy usunąć:

Image

Powyżej widać, że obiekt jest wybierany za pomocą menu kontekstowego (kliknięcie prawym przyciskiem myszy). Wymagane jest potwierdzenie usunięcia:

 

Formularz [index.xhtml] zmienia się w następujący sposób:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">

      <!-- tytuł -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>

      <!-- menu kontekstowe -->
      <p:contextMenu for="personnes">  
        <p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
      </p:contextMenu>  

      <!-- okno dialogowe -->
      <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>
  • wiersze 9–11: definiują menu kontekstowe dla (atrybut for) elementu dataTable z wiersza 21 (atrybut id). Menu kontekstowe pojawia się więc po kliknięciu prawym przyciskiem myszy na tabeli osób,
  • wiersz 10: nasze menu ma tylko jedną opcję (tag menuItem). Po kliknięciu tej opcji wykonywany jest kod JavaScript z atrybutu onclick. Kod JavaScript [confirmation.show()] powoduje wyświetlenie okna dialogowego z linii 14 (atrybut widgetVar). Wygląda ono następująco:
  • wiersz 14: atrybut „message” powoduje wyświetlenie tekstu [3], atrybut „header” powoduje wyświetlenie tekstu [1], atrybut „severity” powoduje wyświetlenie ikony [2],
  • wiersz 16: wyświetla [4]. Po kliknięciu osoba zostaje usunięta (atrybut „action”), a następnie okno dialogowe zostaje zamknięte (atrybut „oncomplete”). Atrybut „oncomplete” to kod JavaScript, który jest wykonywany po zakończeniu operacji po stronie serwera,
  • wiersz 17: powoduje wyświetlenie [5]. Po kliknięciu okno dialogowe zostaje zamknięte, a osoba nie jest usuwana.

5.17. Przykład: mv-pf-14: dataTable - 5

Ten projekt pokazuje, że możliwe jest uzyskanie odpowiedzi z serwera po wykonaniu wywołania AJAX. W tym celu wykorzystuje się atrybut oncomplete wywołania AJAX:

 

Formularz [index.xhtml] zmienia się w następujący sposób:


...
  <ui:composition template="layout.xhtml">
    <ui:define name="contenu">
...
<!-- okno dialogowe 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) {  
          // błąd?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
      </script> 
...
      </p:dataTable>
    </ui:define>
  </ui:composition>
</html>
  • wiersz 7: atrybut oncomplete wywołuje funkcję JavaScript z wierszy 13–18,
  • wiersz 13: sygnatura metody musi wyglądać tak. args to słownik, który model po stronie serwera może uzupełniać,
  • wiersz 15: sprawdzamy, czy słownik args posiada atrybut o nazwie „msgErreur”. Jeśli tak, to jest on wyświetlany (wiersz 16).

W modelu metoda [retirerPersonne] wygląda następująco:


public void retirerPersonne() {
    // losowe usunięcie
    int i = (int) (Math.random() * 2);
    if (i == 0) {
      // usuwamy wybraną osobę
      personnes.remove(personneChoisie);
    } else {
      // zwracany jest błąd
      String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
      RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
    }
  }
  • wiersz 3: generowana jest losowa liczba 0 lub 1,
  • wiersze 4–6: jeśli jest to 0, osoba wybrana przez użytkownika jest usuwana z listy osób,
  • wiersz 9: w przeciwnym razie generowany jest międzynarodowy komunikat o błędzie:

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.
  • wiersz 10: skomplikowana instrukcja, której celem jest dodanie do wspomnianego wcześniej słownika args atrybutu o nazwie „msgErreur” z wartością msgErreur utworzoną w wierszu 9. Atrybut ten jest następnie pobierany przez metodę JavaScript o nazwie [index.xhtml]:

      <!-- JavaScript -->
      <script type="text/javascript">  
        function handleRequest(xhr, status, args) {  
          // błąd?
          if(args.msgErreur) {  
            alert(args.msgErreur);  
          }  
        }  
</script> 

5.18. Przykład: mv-pf-15: pasek narzędzi

W tym projekcie tworzymy pasek narzędzi:

Pasek narzędzi to element zaznaczony ramką powyżej. Uzyskuje się go za pomocą kodu XHTML, wynikającego z kodu [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">
      <!-- tytuł -->
      <h2><h:outputText value="#{msg['app.titre2']}"/></h2>

      <!-- pasek narzędzi-->
      <p:toolbar>
        <p:toolbarGroup align="left">  
          ...  
        </p:toolbarGroup>
        <p:toolbarGroup align="right">  
          ...  
        </p:toolbarGroup>  
      </p:toolbar>
    </ui:define>
  </ui:composition>
</html>
  • wiersze 15–22: pasek narzędzi,
  • wiersze 16–18: definiują grupę komponentów po lewej stronie paska,
  • wiersze 19–21: to samo dotyczy komponentów po prawej stronie.

Komponenty po lewej stronie paska narzędzi to:


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

Wyświetlają poniższy widok:

  • wiersz 2: wyświetla [1],
  • wiersz 3: wyświetla odstęp o szerokości 30 pikseli [2],
  • wiersze 4–6: wyświetlają listę rozwijaną z listą osób [3],
  • wiersz 7: wyświetla separator [4],
  • wiersz 8: wyświetla przycisk [5] służący do usunięcia osoby wybranej z listy rozwijanej. Przycisk posiada ikonę. Są to ikony JQuery i UI. Ich listę można znaleźć w URL, [http://jqueryui.com/themeroller/] i [6]:
  • aby poznać nazwę ikony, wystarczy najechać na nią kursorem myszy. Następnie nazwa ta jest wykorzystywana w atrybucie icon elementu <commandButton>, na przykład icon="ui-icon-trash". Należy zauważyć, że w powyższym przykładzie nadana nazwa to .ui-icon-trash, a w atrybucie icon, usuwa się początkowy kropkę z tej nazwy
  • wiersz 9: tworzy dymek pomocy dla przycisku (atrybut „for”). Po najechaniu kursorem na przycisk wyświetla się komunikat pomocy [7].

Szablon powiązany z tymi komponentami wygląda następująco:


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 {

  // szablon
  private List<Personne> personnes;
  private int personneId;

  // konstruktor
  public Form() {
    // inicjalizacja listy osób
    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() {
    // wyszukiwanie wybranej osoby
    int i = 0;
    for (Personne personne : personnes) {
      // osoba bieżąca = osoba zaznaczona?
      if (personne.getId() == personneId) {
        // usuwanie aktualnej osoby z listy
        personnes.remove(i);
        // zakończono
        break;
      } else {
        // następna osoba
        i++;
      }
    }
  }

  // metody pobierające i ustawiające
  ...
}

Komponenty po prawej stronie paska narzędzi to:


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

Wyświetlają one poniższy widok:

 
  • wiersze 6–9: przycisk menu. Zawiera on opcje menu,
  • wiersz 7: opcja zmiany języka formularza na francuski,
  • wiersz 8: opcja zmiany języka na angielski.

5.19. Conclusion

Wiemy już wystarczająco dużo, aby przenieść naszą przykładową aplikację na Primefaces. Omówiliśmy zaledwie około piętnastu komponentów, podczas gdy biblioteka zawiera ich ponad 100. Zachęcamy czytelnika do poszukiwania brakujących komponentów bezpośrednio na stronie Primefaces.

5.20. Testy w środowisku Eclipse

Projekty Maven są dostępne na stronie z przykładami [1]:

Po zaimportowaniu do Eclipse można je uruchomić [2]. Wybieramy Tomcat w [3]. Następnie wyświetlają się one w wewnętrznej przeglądarce Eclipse [3].