5. Вступ до бібліотеки компонентів PrimeFaces
5.1. Роль Primefaces у додатку JSF
Повернемося до архітектури додатка JSF, яку ми розглядали на початку цього документа:
![]() |
Сторінки JSF були побудовані з використанням трьох бібліотек тегів:
- рядок 2: теги <h:x> з простору імен [http://java.sun.com/jsf/html], які відповідають тегам HTML,
- рядок 3: теги <f:y> з простору імен [http://java.sun.com/jsf/core], які відповідають тегам JSF,
- рядок 4: теги <ui:z> з простору імен [http://java.sun.com/jsf/facelets], які відповідають тегам фаселетів.
Щоб створити сторінки JSF, ми додамо четверту бібліотеку тегів — теги компонентів Primefaces.
- рядок 3: теги <p:z> з простору імен [http://primefaces.org/ui] відповідають компонентам Primefaces.
Це єдина зміна, яка відбудеться. Отже, вона відображається у видах. Обробники подій та моделі залишаються такими, якими вони були з JSF. Це важливий момент, який слід усвідомити.
Використання компонентів Primefaces дозволяє створювати веб-інтерфейси, що є більш зручними для користувачів завдяки численним компонентам цієї бібліотеки, та більш плавними завдяки технології AJAX, яку вона використовує нативно. У цьому випадку йдеться про багатофункціональні інтерфейси або RIA (Rich Internet Application).
Попередня архітектура JSF перетвориться на наступну архітектуру PF (Primefaces):
![]() |
5.2. Переваги Primefaces
На сайті Primefaces [http://www.primefaces.org/showcase/ui/home.jsf] наведено перелік компонентів, які можна використовувати на сторінці PF:
![]() |
У наступних прикладах ми використаємо дві перші можливості Primefaces:
- деякі з сотні доступних компонентів,
- а також їхню вбудовану поведінку AJAX.
Серед доступних компонентів:
![]() | ![]() | ![]() |
У наших прикладах ми використаємо лише близько п'ятнадцяти з них, але цього буде достатньо, щоб зрозуміти принципи побудови сторінки Primefaces.
5.3. Ознайомлення з Primefaces
Primefaces пропонує приклади використання кожного зі своїх компонентів. Достатньо просто натиснути на відповідне посилання. Розглянемо один із прикладів:
![]() |
- en [1] — приклад для компонента [Spinner],
- у [2] — діалогове вікно, що з’являється після натискання кнопки [Submit].
Тут для нас є три нововведення:
- компонент [Spinner], якого за замовчуванням немає в JSF,
- те саме стосується діалогового вікна,
- і, нарешті, POST, що викликається [Submit], реалізовано за допомогою AJAX. Якщо уважно спостерігати за браузером під час виконання POST, пісочний годинник не відображається. Сторінка не перезавантажується. Вона просто змінюється: на сторінці з’являється новий компонент, у даному випадку — діалогове вікно.
Давайте розглянемо, як це все відбувається. Код XHTML із прикладу виглядає так:
<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>
Перш за все, звернімо увагу, що тут присутні класичні теги JSF: <h:form> у рядку 1, <h:panelGrid> у рядку 3, <h:outputLabel> у рядку 4. Деякі теги JSF повторюються у тегах PF та доповнені: <p:commandButton> рядок 21. Далі зустрічаються теги PF, що відповідають за форматування: <p:panel> рядок 2, <p:outputPanel> рядок 13, <p:dialog> рядок 23. Нарешті, є теги для введення даних: <p:spinner> рядок 5.
Проаналізуємо цей код у відповідності до вигляду:
![]() |
- у [1] — компонент, отриманий за допомогою тегу <p:panel> у рядку 2,
- у [2] — поле введення, отримане шляхом поєднання тегів <p:outputLabel> та <p:spinner>, рядки 6 і 7,
- в [3] — кнопка POST, отримана за допомогою тегу <p:commandButton> у рядку 21,
- у [4] — діалогове вікно з рядків 23–25,
- в [5] — невидимий контейнер для двох компонентів. Він створюється тегом <p:outputPanel> у рядку 13.
Проаналізуємо наступний код, який реалізує дію 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>
Цей код генерує таке відображення:
![]() |
- рядок 1: відображає текст [1]. Водночас є міткою для компонента id=ajaxspinner (атрибут for). Цей компонент відповідає рядку 3 (атрибут id),
- рядки 3–5: відображають компонент [2]. Цей компонент є компонентом введення/відображення, пов’язаним із шаблоном #{spinnerController.number5} (атрибут value),
- рядок 6: відображає компонент [3]. Цей компонент є компонентом відображення, пов’язаним із шаблоном #{spinnerController.number5} (атрибут value),
- рядок 4: тег <p:ajax> додає поведінку AJAX до spinner. Щоразу, коли останній змінює значення, до моделі #{spinnerController.number5}. Після цього виконується оновлення сторінки (атрибут update). Цим атрибутом задається ідентифікатор компонента сторінки, у даному випадку — компонента з рядка 6. Цільовий компонент атрибута update оновлюється відповідно до моделі. Він знову дорівнює #{spinnerController.number5}, тобто значенню spinner. Таким чином, поле [3] повторює введені дані з поля [2].
Тут ми маємо поведінку AJAX — акронім, що означає Asynchronous Javascript And XML. Загалом поведінка AJAX полягає в наступному:
![]() |
- браузер відображає сторінку HTML, яка містить код JavaScript (J у AJAX). Елементи сторінки утворюють об’єкт JavaScript, який називається DOM (Document Object Model),
- сервер розміщує веб-додаток, який створив цю сторінку,
- у [1] на сторінці відбувається подія. Наприклад, інкремент spinner. Ця подія обробляється за допомогою JavaScript,
- У [2] JavaScript виконує POST у веб-додатку. Це відбувається асинхронно (літера «A» у AJAX). Користувач може продовжувати працювати зі сторінкою. Вона не заморожується, але за необхідності її можна заморозити. POST оновлює шаблон сторінки на основі надісланих значень, у даному випадку шаблон #{spinnerController.number5},
- на [3]; веб-додаток повертає JavaScript відповідь XML (X з AJAX) або JSON (JavaScript — об’єктна нотація),
- у [4] JavaScript використовує цю відповідь для оновлення певної області DOM, у даному випадку області id=ajaxspinnervalue.
При використанні JSF та Primefaces код JavaScript генерується Primefaces. Ця бібліотека спирається на бібліотеку JavaScript JQuery. Так само компоненти Primefaces базуються на компонентах із бібліотеки JQuery та UI (User Interface). Отже, JQuery є основою Primefaces.
Повернемося до нашого прикладу і розглянемо POST кнопки [Submit]:
![]() |
Код, пов’язаний із POST, такий:
<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>
- рядок 1: POST викликається кнопкою з рядка 1. У Primefaces теги, що викликають POST, за замовчуванням роблять це у вигляді виклику AJAX. Саме тому ці теги мають атрибут update, який вказує область, що підлягає оновленню після отримання відповіді від сервера. У даному випадку оновлюваною областю є panelGrid у рядку 4. Отже, після повернення POST ця область буде оновлена значеннями, відправленими до моделі. Однак вони знаходяться всередині діалогового вікна, яке за замовчуванням не відображається. Його відображає атрибут oncomplete у рядку 1. Ця подія відбувається після завершення обробки POST. Значенням цього атрибута є код JavaScript. Тут відображається діалогове вікно з id=dialog, тобто те, що вказано в рядку 3 (атрибут widgetVar),
- рядок 3: ми бачимо різні атрибути діалогового вікна. Треба поекспериментувати, щоб зрозуміти, для чого вони призначені.
Ми говорили про модель, але ще не показали її. Ось вона:
Загалом можна діяти так:
- знайти компонент Primefaces, який хочемо використовувати,
- ознайомитися з його прикладом. Приклади Primefaces добре опрацьовані та легко зрозумілі.
5.4. Перший проєкт Primefaces: mv-pf-01
Створимо веб-проект Maven за допомогою NetBeans:
![]() |
- [1, 2, 3]: створюємо проект Maven типу [Web Application],
![]() |
- [4]: сервером буде Tomcat,
- у [5] — згенерований проєкт,
- у [6] його очищають від файлу [index.jsp] та пакета Java,
![]() |
- у [7, 8]: у властивостях проєкту додаємо підтримку Java Server Faces,
![]() |
- до [9], на вкладці [Components] вибираємо бібліотеку компонентів PrimeFaces. NetBeans підтримує й інші бібліотеки компонентів: ICEFaces та RichFaces.
- en[10] — згенерований проєкт. У [11] зверніть увагу на залежність від Primefaces.
Простіше кажучи, проект Primefaces — це звичайний проект JSF, до якого додано залежність від Primefaces. І нічого більше.
Зрозумівши це, ми змінюємо файл [pom.xml], щоб працювати з останніми версіями бібліотек:
<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>
У рядках 26–30 зверніть увагу на репозиторій Maven для Primefaces. Зробивши ці зміни, ми компілюємо проєкт, щоб запустити завантаження залежностей. У результаті отримуємо проєкт [12].
Тепер спробуємо відтворити приклад, який ми розглядали. Сторінка [index.html] виглядає наступним чином:
<?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>
<!-- форма -->
<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()" />
<!-- діалогове вікно -->
<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>
Не забуваємо про рядок 5, який оголошує простір імен бібліотеки тегів Primefaces. Додаємо до проєкту bean, який слугує шаблоном для сторінки:
![]() |
Bean має такий вигляд:
package beans;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class SpinnerController {
// шаблон
private int number1;
private double number2;
private int number3;
private int number4;
private int number5;
// методи getter та setter
...
}
Клас є біном (рядок 6) з областю дії запиту (рядок 7). Оскільки ім’я не вказано, бін має назву класу з першою літерою, написаною з малої літери: spinnerController.
Під час виконання проєкту отримуємо таке:
![]() |
Таким чином, ми щойно продемонстрували, як протестувати приклад, взятий із сайту Primefaces. Усі приклади можна протестувати таким чином.
Далі ми зосередимося лише на деяких компонентах Primefaces. Спочатку ми повернемося до прикладів, які ми розглядали з JSF, і замінимо деякі теги JSF на теги Primefaces. Зовнішній вигляд сторінок дещо зміниться, вони будуть поводитися як AJAX, але відповідні біни змінювати не доведеться. У кожному з наведених нижче прикладів ми обмежимося лише представленням коду XHTML сторінок та відповідних знімків екрана. Читачам пропонується протестувати ці приклади, щоб виявити відмінності між сторінками JSF та PF.
5.5. Приклад mv-pf-02: менеджер подій — інтернаціоналізація — навігація між сторінками
Цей проєкт є адаптацією проєкту JSF [mv-jsf2-02] (параграф 2.4, сторінка 41):
![]() | ![]() |
Проект NetBeans виглядає так:
![]() |
Сторінка [index.html] виглядає так:
<?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>
У рядках 15, 16 та 19 теги <h:commandLink> було замінено на теги <p:commandLink>. Цей тег за замовчуванням поводиться як AJAX, що можна вимкнути, вказавши атрибут ajax="false". Отже, у цьому випадку теги <p:commandLink> поводяться як теги <h:commandLink>: при натисканні на ці посилання відбудеться перезавантаження сторінки.
5.6. Приклад mv-pf-03: верстка за допомогою фаселетів
Цей проєкт демонструє створення сторінок XHTML за допомогою шаблонів facelets із прикладу [mv-jsf2-09] (параграф 2.11):
![]() |
Проєкт NetBeans має таку назву:
![]() |
- у [1] — файли конфігурації проєкту JSF,
- у [2] — сторінки XHTML,
- на [3] — bean підтримки зміни мови,
- у [4] — файли повідомлень,
- у [5] — залежності.
Сторінки проєкту створені за зразком сторінки [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>
- рядок 9: тег <f:view> обрамлює всю сторінку, щоб скористатися можливостями інтернаціоналізації, які він надає,
- рядок 15: форма з ідентифікатором «form». Ця форма становить основну частину сторінки. У цій частині є лише одна динамічна ділянка — рядки 28–30. Саме туди буде вставлено змінну частину сторінки:
![]() |
- область, обведена рамкою вище, оновлюватиметься за допомогою викликів AJAX. Щоб її ідентифікувати, ми включили її в контейнер Primefaces, згенерований тегом <p:outputPanel> (рядок 27). Цей контейнер отримав ім’я «contenu» (атрибут id). Оскільки він знаходиться у формі, яка сама є контейнером з іменем «formulaire», повна назва динамічного поля становить:formulaire:contenu. Перший символ «:» вказує, що ми починаємо з кореня документа, потім переходимо до контейнера з іменем «formulaire», а потім — до контейнера з іменем «contenu». Складність із AJAX полягає в тому, щоб правильно назвати поля, які потрібно оновити за допомогою виклику AJAX. Найпростіше — переглянути вихідний код отриманої сторінки HTML:
Зверху видно, що тег <h:outputPanel> згенерував тег HTML <span>. У цьому прикладі відносна назва «formulaire:contenu» (без початкового «:») та повна назва «:formulaire:contenu» (з початковим «:») позначають один і той самий об’єкт.
Слід зауважити, що виклики AJAX (<p:commandButton>, <p:commandLink>), які оновлюють динамічну зону, матимуть атрибут update=":formulaire:contenu".
Сторінка [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">
<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>
- у рядку 8 шаблоном для [index.xhtml] є щойно представлена сторінка [layout.xhtml],
- у рядку 9 — це область ідентифікатора вмісту, яка оновлюється [index.html]. У цій області є два фрагменти:
- фрагмент [page1.xhtml], рядок 11;
- фрагмент [page2.xhtml], рядок 14.
Ці два фрагменти є взаємовиключними.
- рядок 10: фрагмент [page1.xhtml] відображається, якщо у запиті атрибут page1 має значення true або якщо атрибут page2 відсутній. Це стосується найпершого запиту, у якому жоден із цих атрибутів не буде присутній. У цьому випадку відображатиметься фрагмент [page1.xhtml],
- рядок 11; фрагмент [page2.xhtml] відображається, якщо у запиті атрибут page2 має значення true
Фрагмент [page1.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">
<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>
і відображає такий вміст:
![]() |
- рядки 11 і 12 — два посилання для зміни мови. Ці два посилання викликають функції AJAX (ajax=true). Це значення за замовчуванням. Тому атрибут ajax=true можна не вказувати. Надалі ми цього більше не робитимемо. Зазначимо, що ці два посилання оновлюють область :form:content (атрибут update), яка виділена рамкою вище,
- рядок 15: навігаційне посилання AJAX, яке також оновлює область :formulaire:contenu,
- рядок 16: використовується тег <h:setPropertyActionListener> для додавання атрибута page2 у запит із значенням true. Це призведе до відображення фрагмента [page2.xhtml] (рядок 6 нижче) на сторінці [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>
Фрагмент [page2.xhtml] працює аналогічно:
![]() |
Код [page2.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">
<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>
З цього прикладу ми винесемо для подальшого використання такі висновки:
- ми будемо використовувати шаблон [layout.xhtml] як шаблон для сторінок,
- динамічна зона буде ідентифікована за ідентифікатором:form:content і оновлюватиметься за допомогою викликів AJAX.
5.7. Приклад mv-pf-04: форма введення даних
Цей проект є адаптацією проектів JSF2 та [mv-jsf2-03] (див. розділ 2.5):
![]() |
Проєкт NetBeans має таку назву:
![]() |
Вище, у [1], наведено сторінки XHTML цього проєкту. Верстку забезпечує шаблон [layout.xhtml], розглянутий раніше. Сторінка [index.xhtml] є єдиною сторінкою проекту. Вона відображається в області :formulaire:contenu. Її код такий:
<?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>
Вона просто відображає фрагмент [page1.xhtml]. Цей фрагмент є еквівалентом форми, розглянутої у прикладі [mv-jsf2-03]. Нагадаємо, що мета останньої полягала у демонстрації тегів введення даних JSF. Ці теги тут були замінені на теги Primefaces.
PanelGrid
Для форматування елементів [page1.xhtml] ми використовуємо тег <p:panelGrid>. Наприклад, для двох посилань на мови:
<!-- мови -->
<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>
Це дає такий результат:
Інший варіант тегу <p:panelGrid> виглядає так:
<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>
Рядки та стовпці таблиці позначаються тегами <p:row> та <p:column>.
Рядки 3–12 визначають заголовок таблиці:
Рядки 14–25 визначають рядок таблиці:
Рядки 27–35 визначають нижній колонтитул таблиці:
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>
![]() |
У рядку 7 атрибут feedback=true дозволяє отримати зворотний зв'язок щодо якості [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>
![]() |
У рядку 7 тег <p:editor> відображає редактор з розширеними можливостями, що дозволяє форматувати текст (шрифт, розмір, колір, вирівнювання тощо). На сервер надсилається код HTML введеного тексту [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>
![]() |
У рядку 14 слід звернути увагу, що посилання [Raz] оновлює AJAX у полі :form:selectManyMenu, яке відповідає компоненту в рядку 6. Проте слід знати, що під час оновлення POST AJAX усі значення форми надсилаються. Отже, оновлюється вся модель. Однак за допомогою цієї моделі оновлюється лише поле :form:selectManyMenu.
selectBooleanCheckbox
<p:row>
<p:column>
<h:outputText value="selectBooleanCheckbox"/>
</p:column>
<p:column>
<h:outputLabel for="selectBooleanCheckbox" value="#{msg['form.selectBooleanCheckboxPrompt']}"/>
<p:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
</p:column>
<p:column>
<h:outputText id="selectBooleanCheckboxValue" value="#{form.selectBooleanCheckbox}"/>
</p:column>
</p:row>
selectManyCheckbox
<p:row>
<p:column>
<h:outputText value="selectManyCheckbox"/>
</p:column>
<p:column>
<h:outputLabel for="selectManyCheckbox" value="#{msg['form.selectManyCheckboxPrompt']}"/>
<p:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
<f:selectItem itemValue="1" itemLabel="rouge"/>
<f:selectItem itemValue="2" itemLabel="bleu"/>
<f:selectItem itemValue="3" itemLabel="blanc"/>
<f:selectItem itemValue="4" itemLabel="noir"/>
</p:selectManyCheckbox>
</p:column>
<p:column>
<h:outputText id="selectManyCheckboxValue" value="#{form.selectManyCheckboxValue}"/>
</p:column>
</p:row>
selectOneRadio
<p:row>
<p:column>
<h:outputText value="selectOneRadio"/>
</p:column>
<p:column>
<h:outputLabel for="selectOneRadio" value="#{msg['form.selectOneRadioPrompt']}"/>
<p:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}" >
<f:selectItem itemValue="1" itemLabel="voiture"/>
<f:selectItem itemValue="2" itemLabel="vélo"/>
<f:selectItem itemValue="3" itemLabel="scooter"/>
<f:selectItem itemValue="4" itemLabel="marche"/>
</p:selectOneRadio>
</p:column>
<p:column>
<h:outputText id="selectOneRadioValue" value="#{form.selectOneRadio}"/>
</p:column>
</p:row>
5.8. Приклад: mv-pf-05: динамічні списки
Цей проект є портированою версією проектів JSF2 та [mv-jsf2-04] (див. розділ 2.6):

Цей проєкт не додає нових тегів Primefaces порівняно з попереднім проєктом. Тому ми не будемо його коментувати. Він входить до переліку прикладів, доступних для читача на сайті документа.
5.9. Приклад: mv-pf-06: навігація – сесія – обробка винятків
Цей проєкт є портуванням проєкту JSF2 [mv-jsf2-05] (див. параграф 2.7):
![]() |
Знову ж таки, цей приклад не містить нових тегів Primefaces. Ми прокоментуємо лише таблицю посилань, виділену рамкою вище:
<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>
- усі посилання мають атрибут ajax=false. Отже, відбувається звичайне завантаження сторінки,
- зверніть увагу на рядки 2 та 4, де показано, як здійснити перенаправлення.
5.10. Приклад: mv-pf-07: перевірка та перетворення введених даних
Цей проєкт є портуванням проєкту JSF2 [mv-jsf2-06] (див. параграф 2.8):

Додаток вводить два нових теги, тег <p:messages>:
<p:messages globalOnly="true"/>

та тег <p:message>:
<p:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<p:message for="saisie1" styleClass="error"/>
Порівняно з тегом <h:message> з JSF, тег <p:message> з PF вносить такі зміни:
- зовнішній вигляд повідомлення про помилку відрізняється від [1],
- поле, в якому допущено помилку, обведене червоною рамкою [2].
5.11. Приклад: mv-pf-08: події, пов’язані зі зміною стану компонентів
Цей проект є портуванням проектів JSF2 та [mv-jsf2-07] (див. розділ 2.9):

Проект JSF ввів поняття listeners. Управління listener за допомогою Primefaces здійснювалося інакше.
З JSF:
З Primefaces:
- рядок 2: тег <h:selectOneMenu> без атрибута valueChangeListener,
- рядок 4: тег <p:ajax> додає поведінку AJAX до свого батьківського тегу <h:selectOneMenu>. За замовчуванням він реагує на подію «зміна значення» списку combo1. При настанні цієї події значення форми, до якої вона належить, будуть відправлені на сервер за допомогою виклику AJAX. Таким чином, модель оновлюється. Цю нову модель використовують для оновлення випадаючого списку, позначеного combo2 (рядок 10). Зверніть увагу, що в рядку 4 виклик AJAX не виконує жодного методу моделі. Це тут не потрібно. Ми просто хочемо змінити модель за допомогою POST на основі введених значень.
5.12. Приклад: mv-pf-09: введення з підказками
Цей проєкт містить теги введення даних, специфічні для Primefaces, які полегшують введення певних типів даних:
![]() |
5.12.1. Проєкт NetBeans
Проєкт NetBeans виглядає так:
![]() |
Цінність проекту полягає в:
- єдиній сторінці [index.html], яку він відображає,
- шаблоні [Form.java] цієї сторінки.
5.12.2. Шаблон
Форма містить чотири поля введення даних, пов’язані з наступною моделлю:
package forms;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@SessionScoped
public class Form implements Serializable {
private Date calendrier;
private Integer slider = 100;
private Integer spinner = 1;
private String autocompleteValue;
public Form() {
}
public List<String> autocomplete(String query) {
...
}
// методи getter та setter
...
}
Ці чотири записи пов’язані з полями рядків 14–17.
5.12.3. Форма
Форма має такий вигляд:
<?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"/>
<!-- календар -->
...
<!-- слайдер -->
...
<!-- спінер -->
...
<!-- автозаповнення -->
...
</p:panelGrid>
</ui:define>
</ui:composition>
</html>
Розглянемо чотири поля для введення даних.
5.12.4. Календар
Тег <p:calendar> дозволяє вибрати дату з календаря. Цей тег підтримує різні атрибути.
<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>
У рядку 2 вказано, що дата має відображатися у форматі «дд/мм/рррр», а часовий пояс — це часовий пояс Парижа. Коли курсор розміщується в полі введення, з’являється календар:
![]() |
5.12.5. Повзунок
Тег <p:slider> дозволяє ввести ціле число, пересуваючи курсор уздовж смуги:
Код тегу такий:
<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}"/>
- рядок 3: тут ми бачимо класичний тег <p:inputText>, який дозволяє ввести ціле число. Це число також можна ввести за допомогою повзунка,
- рядок 4: тег <p:slider> пов’язаний із тегом введення <p:inputText> (атрибут for). Для нього встановлюються мінімальне та максимальне значення.
5.12.6. Спінер
Ми вже мали нагоду представити цей компонент:
<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}"/>
У рядку 3 спінер дозволяє ввести ціле число від 1 до 12. Число можна ввести безпосередньо в поле введення спінера або скористатися стрілками для збільшення/зменшення введеного числа.
5.12.7. Автозаповнення
Підказка при введенні даних полягає у введенні перших символів. Після цього у випадаючому списку з’являються варіанти. Можна вибрати один із них. Цей компонент використовують замість випадаючих списків, коли їхній вміст занадто великий. Припустимо, що ми хочемо запропонувати випадаючий список міст Франції. Це кілька тисяч міст. Якщо дозволити користувачеві ввести перші три символи назви міста, можна запропонувати йому скорочений список міст, що починаються з цих символів.
![]() |
Код цього компонента такий:
<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/>
Тег <p:autoComplete> у рядку 2 забезпечує функцію автозаповнення. Параметр, який нас тут цікавить, — це атрибут completeMethod, значенням якого є назва методу моделі, що відповідає за формування пропозицій, які відповідають символам, введеним користувачем. Цей метод має такий вигляд:
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;
}
- рядок 1: метод отримує як параметр рядок символів, введених користувачем у поле введення. Він повертає список пропозицій,
- рядки 4–6: формується список із 10 пропозицій, який містить символи, отримані як параметри, та додає до них цифру від 0 до 9.
5.12.8. Тег <p:growl>
Тег <p:growl> є можливою заміною тегу <p:messages>, який відображає повідомлення про помилки у формі.
<p:growl id="messages" autoUpdate="true"/>
У наведеному вище прикладі атрибут id не використовується. Атрибут autoUpdate=true вказує, що список повідомлень про помилки має оновлюватися при кожному POST форми.
Припустимо, що ми підтверджуємо наступну форму [1]:
![]() |
- у [2] тег <p:growl> відображає повідомлення про помилки, пов’язані з неправильними даними.
5.13. Приклад: mv-pf-10: dataTable - 1
Цей проєкт демонструє тег <p:dataTable>, який використовується для відображення списків даних

5.13.1. Проєкт NetBeans
Проект NetBeans виглядає так:
![]() |
Цікавість проекту полягає в:
- єдиній сторінці [index.html], яку він відображає,
- моделі [Form.java] цієї сторінки та біну [Personne].
5.13.2. Файл повідомлень
Файл [messages_fr.properties] має такий вигляд:
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. Модель
Bean [Personne] представляє особу:
package forms;
import java.io.Serializable;
public class Personne implements Serializable{
// дані
private int id;
private String nom;
private String prénom;
// конструктори
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);
}
// геттери та сеттери
...
}
Шаблон сторінки [index.xhtml] відповідає такому класу [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{
// модель
private List<Personne> personnes;
private int personneId;
// конструктор
public Form() {
// ініціалізація списку осіб
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() {
...
}
// геттери та сеттери
...
}
- рядки 9–10: bean має область дії сесії,
- рядки 18–24: конструктор створює список із трьох осіб, який зберігатиметься протягом усього циклу запитів,
- рядок 15: номер особи, яку потрібно видалити зі списку,
- рядки 26–28: метод видалення.
5.13.4. Форма
Форма має такий вигляд: [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>
У результаті з’являється таке вікно (див. вставку нижче):
![]() |
- рядок 12: генерує таблицю, показану у рамці вище. Атрибут value вказує на колекцію, що відображається у таблиці, у даному випадку — список осіб з моделі. Атрибут emptyMessage є необов’язковим. Він вказує на повідомлення, яке слід відобразити, коли список порожній. За замовчуванням це «no records found». У даному випадку це буде:
![]() |
- рядки 13–15: генерують заголовок [1],
- рядки 16–21: генерують стовпець [2],
- рядки 22–27: генерують стовпець [3],
- рядки 28–33: генерують стовпець [4],
- рядки 34–41: формують стовпець [5].
Посилання [Retirer] дозволяє видалити особу зі списку. У рядку [38] цю операцію виконує метод [Form].retirerPersonne. Для цього потрібно знати номер особи, яку потрібно видалити. Він вказаний у рядку 39. У рядку 38 використано атрибут action. В інших випадках використовувався атрибут actionListener. Я не впевнений, що до кінця розумію функціональну різницю між цими двома атрибутами. Однак на практиці можна помітити, що атрибути, задані тегами <setPropertyActionListener>, встановлюються до виконання методу, вказаного атрибутом action, тоді як для атрибута actionListener це не так. Іншими словами, щойно з’являються параметри, які потрібно передати викликаній дії, слід використовувати атрибут action.
Метод видалення особи такий:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable{
// модель
private List<Personne> personnes;
private int personneId;
public void retirerPersonne() {
// шукаємо вибрану особу
int i = 0;
for (Personne personne : personnes) {
// поточна особа = обрана особа?
if (personne.getId() == personneId) {
// поточна особа видаляється зі списку
personnes.remove(i);
// завершено
break;
} else {
// наступна особа
i++;
}
}
}
...
}
5.14. Приклад: mv-pf-11: dataTable - 2
Цей проєкт демонструє таблицю зі списком даних, у якій можна вибрати рядок:
![]() |
Вибір рядка з таблиці під час виконання POST надсилає до моделі інформацію про вибраний рядок. Отже, більше не потрібно окремого посилання [Retirer] для кожної особи. Достатньо одного посилання для всієї таблиці.
Проєкт NetBeans ідентичний попередньому, за винятком кількох деталей: форми та її шаблону. Форма [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']}"
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>
- рядок 13: атрибут selectionMode дозволяє вибрати режим вибору single або multiple. Тут ми вирішили вибрати лише один рядок,
- рядок 13: атрибут rowkey позначає атрибут відображуваних елементів, який дозволяє однозначно їх вибрати. Тут ми вибрали ідентифікатор обраної особи,
- рядок 13: атрибут selection позначає атрибут моделі, який отримає посилання на вибрану особу. Завдяки попередньому атрибуту rowkey на стороні сервера можна буде обчислити посилання на вибрану особу. Деталей щодо використовуваного методу немає. Можна припустити, що колекція послідовно переглядається з метою пошуку елемента, що відповідає вибраному rowkey. Це означає, що якщо метод, який пов’язує rowkey з selection, є більш складним, то цей метод не можна використовувати,
З огляду на це, метод [Form].retirerPersonne змінюється наступним чином:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable {
// шаблон
private List<Personne> personnes;
private Personne personneChoisie;
// виробник
public Form() {
...
}
public void retirerPersonne() {
// видалити обрану особу
personnes.remove(personneChoisie);
}
// методи getter та setter
...
}
- рядок 9: для кожного POST посилання в рядку 9 ініціалізується посиланням у списку рядка 8 на вибрану особу,
- у 18: це спрощує видалення особи. Пошук, який ми проводили в попередньому прикладі, здійснювався за допомогою тегу <dataTable>.
5.15. Приклад: mv-pf-12: dataTable - 3
Цей проект аналогічний попередньому. Зокрема, вигляд є ідентичним:

Проєкт NetBeans ідентичний попередньому, за винятком кількох деталей, які ми розглянемо. Форма [index.xhtml] виглядає наступним чином:
...
<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>
- у рядку 6 атрибут rowkey зник, а атрибут selection залишився. Зв’язок між атрибутами rowkey та selection тепер здійснюється через клас. Атрибут value у рядку 5 тепер має значення екземпляра інтерфейсу Primefaces SelectableDataModel<T>. Метод [Form].getPersonnes моделі змінюється наступним чином:
public DataTableModel getPersonnes() {
return new DataTableModel(personnes);
}
Таким чином до проекту додається новий bean:
![]() |
Цей bean має такий вигляд:
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> {
// конструктори
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<Personne> personnes = (List<Personne>) getWrappedData();
// ключ — ціле число
int key = Integer.parseInt(rowKey);
// шукаємо вибрану особу
for (Personne personne : personnes) {
if (personne.getId() == key) {
return personne;
}
}
// нічого не знайдено
return null;
}
}
- рядок 7: клас є екземпляром інтерфейсу SelectableDataModel. Цей інтерфейс реалізують щонайменше два класи: ListDataModel, конструктор якого приймає список як параметр, та ArrayDataModel, конструктор якого приймає масив як параметр. У даному випадку наш bean успадковує клас ListDataModel,
- рядки 13–15: конструктор приймає як параметр список осіб, якими ми керуємо. Цей параметр передається батьківському класу,
- рядок 18: метод getRowKey виконує роль атрибута rowkey, який було видалено. Він повинен повертати об’єкт, що дозволяє однозначно ідентифікувати особу, у даному випадку — ідентифікатор особи,
- рядок 23: метод getRowData повинен повернути вибраний об’єкт на основі його rowkey. Отже, у цьому випадку — повернути особу на основі її ідентифікатора. Отримане таким чином посилання буде присвоєно цільовому об’єкту атрибута selection у тезі dataTable, у даному випадку атрибуту selection="#{form.personneChoisie}". Параметром методу є rowkey об’єкта, вибраного користувачем, у вигляді символьного рядка,
- рядки 24–35: повертають посилання на особу, ідентифікатор якої було отримано. Це посилання буде присвоєно моделі [Form].personneChoisie. Отже, метод [retirerPersonne] залишається без змін:
public void retirerPersonne() {
// видалено обрану особу
personnes.remove(personneChoisie);
}
Цю техніку слід використовувати, коли зв’язок між атрибутами rowkey та selection не є простим зв’язком між властивістю (rowkey) та об’єктом (selection).
5.16. Приклад: mv-pf-13: dataTable - 4
Цей проект аналогічний попередньому, за винятком того, що змінюється спосіб вибору особи, яку потрібно видалити:

Вище ми бачимо, що об’єкт вибирається за допомогою контекстного меню (правий клік). Вимагається підтвердження видалення:
![]() |
Форма [index.xhtml] змінюється наступним чином:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<!-- заголовок -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- контекстне меню -->
<p:contextMenu for="personnes">
<p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
</p:contextMenu>
<!-- діалогове вікно -->
<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>
- рядки 9–11: визначають контекстне меню для (атрибут for) dataTable із рядка 21 (атрибут id). Отже, це контекстне меню з’являється при клацанні правою кнопкою миші на таблиці осіб,
- рядок 10: наше меню має лише один пункт (тег menuItem). При натисканні на цей пункт виконується код JavaScript з атрибута onclick. Код JavaScript [confirmation.show()] викликає діалогове вікно з рядка 14 (атрибут widgetVar). Воно має такий вигляд:
![]() |
- рядок 14: атрибут message відображає [3], атрибут header — [1], атрибут severity — піктограму [2],
- рядок 16: відображає [4]. При натисканні користувач видаляється (атрибут action), після чого діалогове вікно закривається (атрибут oncomplete). Атрибут oncomplete — це код JavaScript, який виконується після завершення серверної операції,
- рядок 17: відображає [5]. При натисканні діалогове вікно закривається, а особа не видаляється.
5.17. Приклад: mv-pf-14: dataTable - 5
Цей проект демонструє, що після виконання виклику AJAX можна отримати відповідь від сервера. Для цього використовується атрибут oncomplete виклику AJAX:
![]() |
Форма [index.xhtml] змінюється наступним чином:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
...
<!-- діалогове вікно 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) {
// помилка?
if(args.msgErreur) {
alert(args.msgErreur);
}
}
</script>
...
</p:dataTable>
</ui:define>
</ui:composition>
</html>
- рядок 7: атрибут oncomplete викликає функцію JavaScript у рядках 13–18,
- рядок 13: сигнатура методу має бути такою. args — це словник, який серверна модель може доповнювати,
- рядок 15: перевіряється, чи має словник args атрибут із назвою «msgErreur». Якщо так, то він відображається (рядок 16).
У моделі метод [retirerPersonne] виглядає так:
public void retirerPersonne() {
// випадкове видалення
int i = (int) (Math.random() * 2);
if (i == 0) {
// видаляємо обрану особу
personnes.remove(personneChoisie);
} else {
// повертається помилка
String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
}
}
- рядок 3: генерується випадкове число 0 або 1,
- рядки 4–6: якщо це 0, особа, обрана користувачем, видаляється зі списку осіб,
- рядок 9: інакше формується інтернаціоналізоване повідомлення про помилку:
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.
- рядок 10: складна інструкція, метою якої є додавання до словника args, про який ми вже згадували, атрибута з назвою «msgErreur» із значенням msgErreur, сформованим у рядку 9. Цей атрибут потім отримується методом JavaScript [index.xhtml]:
<!-- JavaScript -->
<script type="text/javascript">
function handleRequest(xhr, status, args) {
// помилка?
if(args.msgErreur) {
alert(args.msgErreur);
}
}
</script>
5.18. Приклад: mv-pf-15: панель інструментів
У цьому проєкті ми створюємо панель інструментів:
![]() |
Панель інструментів — це компонент, виділений рамкою вище. Вона створюється за допомогою коду XHTML, що наведено нижче [index.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<!-- заголовок -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- панель інструментів-->
<p:toolbar>
<p:toolbarGroup align="left">
...
</p:toolbarGroup>
<p:toolbarGroup align="right">
...
</p:toolbarGroup>
</p:toolbar>
</ui:define>
</ui:composition>
</html>
- рядки 15–22: панель інструментів,
- рядки 16–18: визначають групу компонентів ліворуч від панелі,
- рядки 19–21: те саме для компонентів праворуч.
Компоненти ліворуч від панелі інструментів такі:
<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>
Вони відображають такий вигляд:
![]() |
- рядок 2: відображає [1],
- рядок 3: відображає пробіл розміром 30 пікселів [2],
- рядки 4–6: відображають випадаючий список із переліком осіб [3],
- рядок 7: відображає роздільник [4],
- рядок 8: відображає кнопку [5], призначену для видалення особи, вибраної у випадаючому списку. Кнопка має піктограму. Ці піктограми — це JQuery та UI. Їхній перелік наведено в URL, [http://jqueryui.com/themeroller/] та [6]:
![]() |
- щоб дізнатися назву іконки, достатньо навести на неї курсор миші. Потім ця назва використовується в атрибуті icon компонента <commandButton>, наприклад icon="ui-icon-trash". Зверніть увагу, що у наведеному вище прикладі ім’я буде .ui-icon-trash, а початкову крапку з цього імені у атрибуті icon,
- рядок 9: створює підказку для кнопки (атрибут for). Коли курсор наводиться на кнопку, з’являється підказка [7].
Шаблон, пов’язаний із цими компонентами, має такий вигляд:
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 {
// шаблон
private List<Personne> personnes;
private int personneId;
// конструктор
public Form() {
// ініціалізація списку осіб
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() {
// пошук вибраної особи
int i = 0;
for (Personne personne : personnes) {
// поточна особа = вибрана особа?
if (personne.getId() == personneId) {
// поточна особа видаляється зі списку
personnes.remove(i);
// завершено
break;
} else {
// наступна особа
i++;
}
}
}
// методи getter та setter
...
}
Компоненти праворуч від панелі інструментів такі:
<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>
Вони відображають такий вигляд:
![]() |
- рядки 6–9: кнопка меню. Вона містить пункти меню,
- рядок 7: опція перемикання форми на французьку мову,
- рядок 8: опція переходу на англійську мову.
5.19. Conclusion
Ми знаємо достатньо, щоб перенести наш приклад додатка на Primefaces. Ми розглянули лише близько п’ятнадцяти компонентів, хоча бібліотека містить понад 100. Читачеві пропонується знайти компонент, якого йому бракує, безпосередньо на сайті Primefaces.
5.20. Тестування за допомогою Eclipse
Проєкти Maven доступні на сайті прикладів [1]:
![]() |
Після імпортування в Eclipse їх можна запустити [2]. У [3] слід вибрати Tomcat. Після цього вони відображаються у вбудованому браузері Eclipse [3].
![]() |



















































