5. PrimeFaces 组件库简介
5.1. PrimeFaces 在 JSF 应用程序中的作用
让我们回到本文开头所探讨的 JSF 应用程序架构:
![]() |
JSF 页面是通过以下三个标签库构建的:
- 第 2 行:来自 [http://java.sun.com/jsf/html] 命名空间的 <h:x> 标签,它们对应于 HTML 标签,
- 第 3 行:来自 [http://java.sun.com/jsf/core] 命名空间的 <f:y> 标签,对应于 JSF 标签,
- 第 4 行:来自 [http://java.sun.com/jsf/facelets] 命名空间的 <ui:z> 标签,对应于 Facelet 标签。
为了构建 JSF 页面,我们将添加第四个标签库,即 PrimeFaces 组件的标签库。
- 第 3 行:来自 [http://primefaces.org/ui] 命名空间的 <p:z> 标签对应于 PrimeFaces 组件。
这是唯一需要进行的更改。因此,它会出现在视图中。事件处理程序和模型与 JSF 中的保持一致。这一点非常重要,需要理解。
使用 PrimeFaces 组件,您可以创建更用户友好的 Web 界面——得益于该库中的众多组件,以及其原生支持的 AJAX 技术带来的更流畅的交互体验。此类界面被称为富界面或 RIA(富互联网应用程序)。
原有的 JSF 架构将转变为以下 PF(PrimeFaces)架构:
![]() |
5.2. PrimeFaces 的优势
PrimeFaces 网站 [http://www.primefaces.org/showcase/ui/home.jsf] 提供了可在 PF 页面中使用的组件列表:
![]() |
在接下来的示例中,我们将使用 PrimeFaces 的前两个功能:
- 其中约一百个组件中的部分,
- 以及它们原生的 AJAX 行为。
在可用的组件中:
![]() | ![]() | ![]() |
在我们的示例中,我们将仅使用其中大约十五个,但这足以帮助您理解构建 PrimeFaces 页面的基本原理。
5.3. 学习 PrimeFaces
PrimeFaces 为每个组件都提供了使用示例。只需点击链接即可。让我们来看一个示例:
![]() |
- 在 [1] 中,是 [Spinner] 组件的示例,
- 在 [2] 中,是点击 [Submit] 按钮后显示的对话框。
这里有三个新功能:
- [Spinner]组件,该组件在JSF中默认并不存在,
- 对话框也是如此,
- 最后,由 [Submit] 按钮触发的 POST 请求是通过 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 标签:第 1 行的 <h:form>、第 3 行的 <h:panelGrid> 以及第 4 行的 <h:outputLabel>。PF 复用了部分 JSF 标签并对其进行了增强:第 21 行的 <p:commandButton>。 接下来,我们看到 PF 格式化标签:第 2 行的 <p:panel>、第 13 行的 <p:outputPanel> 以及第 23 行的 <p:dialog>。最后,还有输入标签:第 5 行的 <p:spinner>。
让我们结合视图来分析这段代码:
![]() |
- 在 [1] 中,第 2 行使用 <p:panel> 标签创建的组件,
- 在 [2] 中,第 6 行和第 7 行通过组合 <p:outputLabel> 和 <p:spinner> 标签创建的输入字段,
- 在 [3] 中,第 21 行使用 <p:commandButton> 标签创建的 POST 按钮,
- 在 [4] 中,第 23–25 行生成的对话框,
- 在 [5] 中,是一个用于容纳两个组件的不可见容器。它由第 13 行中的 <p:outputPanel> 标签创建。
让我们分析以下实现 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 的组件的标签(用于 id 属性)。该组件即第 3 行中的组件(id 属性),
- 第 3–5 行:显示组件 [2]。该组件是一个与模型 #{spinnerController.number5} 关联的输入/显示组件(value 属性),
- 第 6 行:显示组件 [3]。该组件是一个显示组件,关联到模型 #{spinnerController.number5}(value 属性),
- 第 4 行:<p:ajax> 标签为旋转按钮添加了 AJAX 行为。每次旋转按钮的值发生变化时,都会向 #{spinnerController.number5} 模型发送一个包含该值的 POST 请求(process="@this" 属性)。 操作完成后,页面将被更新(update 属性)。该属性的值为页面上某个组件的 ID,本例中即第 6 行中的组件。update 属性所指向的组件随后将根据模型进行更新。该模型仍是 #{spinnerController.number5},即旋转按钮的当前值。因此,字段 [3] 的值将跟随字段 [2] 的输入。
这是一种 AJAX 行为,AJAX 是 Asynchronous JavaScript and XML(异步 JavaScript 和 XML)的首字母缩写。一般而言,AJAX 行为的工作原理如下:
![]() |
- 浏览器显示一个包含 JavaScript 代码(即 AJAX 中的“J”)的 HTML 页面。该页面的元素构成一个名为 DOM(文档对象模型)的 JavaScript 对象,
- 服务器托管着生成该页面的 Web 应用程序,
- 在[1]中,页面上发生了一个事件。例如,加载图标发生了变化。该事件由JavaScript处理,
- 在 [2],JavaScript 向 Web 应用程序发送一个 POST 请求。该请求以异步方式发送(即 AJAX 中的“A”)。用户可以继续操作该页面。页面不会被冻结,但在必要时可以冻结。POST 请求根据提交的值更新页面模型,此处模型为 #{spinnerController.number5},
- 在 [3],Web 应用程序将 XML(AJAX 中的 X)或 JSON(JavaScript 对象表示法)响应发回给 JavaScript,
- 在 [4] 中,JavaScript 使用此响应更新 DOM 的特定区域,此处为 id=ajaxspinnervalue 的区域。
在使用 JSF 和 PrimeFaces 时,JavaScript 代码由 PrimeFaces 生成。该库依赖于 jQuery JavaScript 库。同样,PrimeFaces 组件也依赖于 jQuery UI(用户界面)组件库中的组件。因此,jQuery 构成了 PrimeFaces 的基础。
让我们回到我们的示例,现在来看来自 [Submit] 按钮的 POST 请求:
![]() |
与该 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` 属性,用于指定在收到服务器响应后需要更新的区域。在此处,待更新的区域是第 4 行的 panelGrid。因此,当 POST 响应返回时,该区域将使用提交到模型的值进行更新。 然而,这些值被封装在一个默认隐藏的对话框中。正是第 1 行中的 oncomplete 属性将其显示出来。该事件发生在 POST 处理结束时。该属性的值是 JavaScript 代码。在此,我们显示 id=dialog 的对话框,即第 3 行(widgetVar 属性)中定义的那个,
- 第 3 行:我们可以看到对话框的各种属性。您需要通过实验来了解它们的功能。
我们之前提到了模型,但尚未展示。如下所示:
一般而言,您可以按照以下步骤操作:
- 确定您想要使用的 PrimeFaces 组件,
- 研究其示例。PrimeFaces 的示例编写精良且通俗易懂。
5.4. 第一个 PrimeFaces 项目:mv-pf-01
让我们使用 NetBeans 构建一个 Maven Web 项目:
![]() |
- [1, 2, 3]:创建一个类型为 [Web Application] 的 Maven 项目,
![]() |
- [4]:服务器将使用 Tomcat,
- 在 [5] 中,生成的项目,
- 在 [6] 中,删除 [index.jsp] 文件和 Java 包,
![]() |
- 在 [7, 8] 中:在项目属性中,我们添加了对 Java Server Faces 的支持,
![]() |
- 在 [9] 中,在 [组件] 选项卡中,选择 PrimeFaces 组件库。NetBeans 还支持其他组件库:ICEFaces 和 RichFaces。
- 在 [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 行:请注意 PrimeFaces 的 Maven 仓库。完成这些更改后,构建项目以开始下载依赖项。随后您将获得该项目 [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>
<!-- form -->
<h:form>
<p:panel header="Spinners">
<h:panelGrid id="grid" columns="2" cellpadding="5">
<h:outputLabel for="spinnerBasic" value="Basic Spinner: " />
<p:spinner id="spinnerBasic" value="#{spinnerController.number1}"/>
<h:outputLabel for="spinnerStep" value="Step Factor: " />
<p:spinner id="spinnerStep" value="#{spinnerController.number2}" stepFactor="0.25"/>
<h:outputLabel for="minmax" value="Min/Max: " />
<p:spinner id="minmax" value="#{spinnerController.number3}" min="0" max="100"/>
<h:outputLabel for="prefix" value="Prefix: " />
<p:spinner id="prefix" prefix="$" min="0" value="#{spinnerController.number4}"/>
<h:outputLabel for="ajaxspinner" value="Ajax Spinner: " />
<p:outputPanel>
<p:spinner id="ajaxspinner" value="#{spinnerController.number5}">
<p:ajax update="ajaxspinnervalue" process="@this" />
</p:spinner>
<h:outputText id="ajaxspinnervalue" value="#{spinnerController.number5}"/>
</p:outputPanel>
</h:panelGrid>
</p:panel>
<p:commandButton value="Submit" update="display" oncomplete="dialog.show()" />
<!-- dialog box -->
<p:dialog header="Values" widgetVar="dialog" showEffect="fold" hideEffect="fold">
<h:panelGrid id="display" columns="2" cellpadding="5">
<h:outputText value="Value 1: " />
<h:outputText value="#{spinnerController.number1}" />
<h:outputText value="Value 2: " />
<h:outputText value="#{spinnerController.number2}" />
<h:outputText value="Value 3: " />
<h:outputText value="#{spinnerController.number3}" />
<h:outputText value="Value 4: " />
<h:outputText value="#{spinnerController.number4}" />
<h:outputText value="Value 5: " />
<h:outputText value="#{spinnerController.number5}" />
</h:panelGrid>
</p:dialog>
</h:form>
</h:body>
</html>
别忘了第 5 行,该行声明了 PrimeFaces 标签库的命名空间。将作为页面模板的 Bean 添加到项目中:
![]() |
该 Bean 如下所示:
package beans;
import javax.faces.bean.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class SpinnerController {
// model
private int number1;
private double number2;
private int number3;
private int number4;
private int number5;
// getters and setters
...
}
该类是一个 Bean(第 6 行),且具有请求作用域(第 7 行)。由于未指定名称,该 Bean 将采用类名的首字母小写形式:spinnerController。
运行项目后,我们将得到以下结果:
![]() |
我们刚刚演示了如何测试一个来自 PrimeFaces 网站的示例。所有示例都可以通过这种方式进行测试。
接下来,我们将仅关注某些 PrimeFaces 组件。首先,我们将重新审视之前使用 JSF 学习的示例,并将某些 JSF 标签替换为 PrimeFaces 标签。页面的外观会略有变化;它们将具备 AJAX 行为,但相关的 Bean 无需更改。在接下来的每个示例中,我们将仅展示页面的 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:使用 Facelets 进行页面布局
本项目演示了如何使用示例 [mv-jsf2-09](第 2.11 节)中的 Facelets 模板创建 XHTML 页面:
![]() |
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 行:一个带有表单 ID 的表单。该表单构成页面的主体。在此主体内,仅有一个动态部分,即第 28–30 行。页面中可变的部分将插入在此处:
![]() |
- 上方的框选区域将通过 AJAX 调用进行更新。为了标识该区域,我们将其包含在由 <p:outputPanel> 标签生成的 PrimeFaces 容器中(第 27 行)。该容器被命名为 `content`(id 属性)。由于它位于名为 `form` 的表单容器内,因此该动态区域的完整名称为 `form:content`。 第一个冒号(:)表示从文档根节点开始,然后进入名为“form”的容器,再进入名为“content”的容器。AJAX 的一大挑战在于如何为 AJAX 调用要更新的区域命名。最简单的方法是查看接收到的 HTML 页面的源代码:
在上文中,我们可以看到 <h:outputPanel> 标签生成了一个 HTML 标签 <span>。在此示例中,相对名称 form:content(不带前导冒号)和完整名称 :form:content(带前导冒号)都指向同一个对象。
请注意,用于更新动态区域的 AJAX 调用(<p:commandButton>、<p:commandLink>)将具有 update=":form:content" 属性。
[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] 更新的内容 ID 区域。该区域包含两个片段:
- 第 11 行的 [page1.xhtml] 片段;
- 第 14 行的 [page2.xhtml] 片段。
这两个片段互斥。
- 第 10 行:如果请求中的 page1 属性设置为 true,或者 page2 属性不存在,则显示 [page1.xhtml] 片段。这适用于首次请求,因为此时请求中不会包含这两个属性中的任何一个。在这种情况下,将显示 [page1.xhtml] 片段,
- 第 11 行:如果请求中 page2 属性设置为 true,则显示 [page2.xhtml] 片段
[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 导航链接,同样用于更新 :form:content 区域,
- 第 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] 模板作为页面模板,
- 动态区域将通过 id:form:content 标识,并通过 AJAX 调用进行更新。
5.7. 示例 mv-pf-04:输入表单
本项目是 JSF2 项目 [mv-jsf2-03](参见第 2.5 节)的移植版本:
![]() |
NetBeans 项目结构如下:
![]() |
上文中的 [1] 处是该项目的 XHTML 页面。布局由前文提到的 [layout.xhtml] 模板提供。[index.xhtml] 页面是该项目的唯一页面。它显示在 :form:content 区域中。其代码如下:
<?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> 标签。例如,对于两个语言链接:
<!-- languages -->
<p:panelGrid columns="2">
<p:commandLink value="#{msg['form.langue1']}" actionListener="#{changeLocale.setFrenchLocale}" update=":formulaire:contenu"/>
<p:commandLink value="#{msg['form.langue2']}" actionListener="#{changeLocale.setEnglishLocale}" update=":formulaire:contenu"/>
</p:panelGrid>
这将生成以下输出:
<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>
密码
<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>
![]() |
下拉菜单
<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 行:请注意,[Reset] 链接会对 :form:selectManyMenu 字段执行 AJAX 更新,该字段对应第 6 行中的组件。但需要注意的是,在 AJAX POST 过程中,所有表单值都会被提交。因此,整个模型都会被更新。然而,在此模型中,仅 :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>
单选按钮
<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"/>
与 JSF 的 <h:message> 标签相比,PF 的 <p:message> 标签引入了以下变化:
- 错误消息的外观有所不同 [1],
- 错误的输入字段会被红色边框包围 [2]。
5.11. 示例:mv-pf-08:与组件状态变化相关的事件
本项目是 JSF2 项目 [mv-jsf2-07] 的移植版本(参见第 2.9 节):

JSF 项目引入了监听器(listener)的概念。PrimeFaces 对监听器的管理方式则有所不同。
在 JSF 中:
使用 PrimeFaces:
- 第 2 行:不带 valueChangeListener 属性的 <h:selectOneMenu> 标签,
- 第 4 行:<p:ajax> 标签为其父级 <h:selectOneMenu> 标签添加了 AJAX 行为。默认情况下,它会响应 combo1 列表的“值变化”事件。当发生此事件时,其所属表单的值将通过 AJAX 调用提交至服务器。因此,模型随之更新。 我们使用这个新模型来更新标识为 combo2 的下拉列表(第 10 行)。请注意,在第 4 行,AJAX 调用并未执行任何模型方法。在此处这是没有必要的。我们只是希望通过提交输入的值来更新模型。
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) {
...
}
// getters and setters
...
}
这四个输入与第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"/>
<!-- calendar -->
...
<!-- slider -->
...
<!-- spinner -->
...
<!-- autocomplete -->
...
</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 行指定日期应以“dd/mm/yyyy”格式显示,且时区为巴黎。当您将光标置于输入框中时,会显示一个日历:
![]() |
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/>
第 2 行中的 <p:autoComplete> 标签用于启用自动完成功能。这里需要关注的参数是 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行:利用作为参数接收的字符,并向每个字符添加0到9之间的一个数字,构建一个包含10个建议项的列表。
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] 模板,以及 [Person] Bean。
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. 该模型
[Person] Bean 表示一个人:
package forms;
import java.io.Serializable;
public class Personne implements Serializable{
// data
private int id;
private String nom;
private String prénom;
// manufacturers
public Personne(){
}
public Personne(int id, String nom, String prénom){
this.id=id;
this.nom=nom;
this.prénom=prénom;
}
// toString
public String toString(){
return String.format("Personne[%d,%s,%s]", id,nom,prénom);
}
// getter and setters
...
}
[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{
// model
private List<Personne> personnes;
private int personneId;
// manufacturer
public Form() {
// initialization of the list of persons
personnes = new ArrayList<Personne>();
personnes.add(new Personne(1, "dupont", "jacques"));
personnes.add(new Personne(2, "durand", "élise"));
personnes.add(new Personne(3, "martin", "jacqueline"));
}
public void retirerPersonne() {
...
}
// getters and setters
...
}
- 第 9-10 行:该 Bean 具有会话作用域,
- 第 18–24 行:构造函数创建了一个包含三人的列表,该列表将在不同请求之间保持持久化,
- 第 15 行:要从列表中移除的人员的 ID,
- 第 26–28 行:delete 方法。
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 属性是可选的,用于指定列表为空时显示的消息。默认情况下,该消息为“未找到记录”。在此处,它将显示为:
![]() |
- 第 13–15 行:生成标题 [1],
- 第 16–21 行:生成列 [2],
- 第 22–27 行:生成列 [3],
- 第 28–33 行:生成列 [4],
- 第 34–41 行:生成列 [5]。
[Remove] 链接允许您将某人从列表中移除。在第 [38] 行,[Form].removePerson 方法执行此任务。它需要知道要移除的人的 ID。该 ID 在第 39 行提供。在第 38 行,我们使用了 action 属性。在其他情况下,我们曾使用过 actionListener 属性。 我不确定是否完全理解这两个属性的功能差异。但在实践中,我们注意到:由 <setPropertyActionListener> 标签设置的属性是在 action 属性指定的方法执行之前设置的,而 actionListener 属性则并非如此。简而言之,只要需要向被调用的操作传递参数,就必须使用 action 属性。
删除人员的代码如下:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable{
// model
private List<Personne> personnes;
private int personneId;
public void retirerPersonne() {
// search for the selected person
int i = 0;
for (Personne personne : personnes) {
// current person = selected person?
if (personne.getId() == personneId) {
// delete the current person from the list
personnes.remove(i);
// we're done
break;
} else {
// next person
i++;
}
}
}
...
}
5.14. 示例:mv-pf-11: 数据表 - 2
该项目包含一个表格,用于显示数据列表,其中可以选中某一行:
![]() |
在表格中选中某一行时,系统会在 POST 请求期间将该行信息发送至模型。因此,不再需要为每位用户单独设置 [删除] 链接,仅需一个针对整个表格的链接即可。
该 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 属性允许您在单选和多选模式之间进行选择。此处,我们选择仅选择一行,
- 第 13 行:rowkey 属性指定了显示元素的一个属性,以便对其进行唯一选择。此处,我们选择了被选中人员的 ID,
- 第 13 行:selection 属性指定了将接收被选人员引用值的模型属性。 得益于前面的 rowkey 属性,可以在服务器端计算出被选中人员的引用。我们并不了解所用方法的具体细节。可以设想,系统会顺序遍历集合以查找与所选 rowkey 对应的元素。这意味着,如果将 rowkey 与选择关联的方法较为复杂,那么该方法便无法使用,
话虽如此,[Form].removePerson 方法的演变如下:
...
@ManagedBean
@SessionScoped
public class Form implements Serializable {
// model
private List<Personne> personnes;
private Personne personneChoisie;
// manufacturer
public Form() {
...
}
public void retirerPersonne() {
// we remove the chosen person
personnes.remove(personneChoisie);
}
// getters and setters
...
}
- 第 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 属性之间的关联现在通过一个类来建立。第 5 行中 value 属性的值 现在持有 PrimeFaces SelectableDataModel<T> 接口的一个实例。该模型的 [Form].getPeople 方法更新如下:
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> {
// manufacturers
public DataTableModel() {
}
public DataTableModel(List<Personne> personnes) {
super(personnes);
}
@Override
public Object getRowKey(Personne personne) {
return personne.getId();
}
@Override
public Personne getRowData(String rowKey) {
// list of persons
List<Personne> personnes = (List<Personne>) getWrappedData();
// the key is an integer
int key = Integer.parseInt(rowKey);
// search for the selected person
for (Personne personne : personnes) {
if (personne.getId() == key) {
return personne;
}
}
// we found nothing
return null;
}
}
- 第 7 行:该类是 SelectableDataModel 接口的一个实例。至少有两个类实现了该接口:ListDataModel(其构造函数接受列表作为参数)和 ArrayDataModel(其构造函数接受数组作为参数)。在此,我们的 Bean 继承了 ListDataModel 类,
- 第 13–15 行:构造函数将我们管理的人员列表作为参数。该参数会被传递给父类,
- 第 18 行:getRowKey 方法承担了已被移除的 rowkey 属性的作用。它必须返回一个能唯一标识人员的对象,在本例中即人员的 ID,
- 第 23 行:getRowData 方法必须根据 rowkey 返回所选对象。因此,此处它根据 ID 返回一个人员对象。通过这种方式获得的引用将被赋值给 dataTable 标签中 selection 属性的目标对象,即此处的 selection="#{form.personneChoisie}"。该方法的参数是用户所选对象的 rowkey,以字符串形式呈现,
- 第 24–35 行:返回 ID 与接收值匹配的 person 对象的引用。该引用将被赋值给 [Form].personneChoisie 模型。因此,[retirerPersonne] 方法保持不变:
public void retirerPersonne() {
// on enlève la personne choisie
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">
<!-- title -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- contextual menu -->
<p:contextMenu for="personnes">
<p:menuitem value="#{msg['form.supprimer']}" onclick="confirmation.show()"/>
</p:contextMenu>
<!-- dialog box -->
<p:confirmDialog widgetVar="confirmation" message="#{msg['form.suppression.confirmation']}"
header="#{msg['form.suppression.message']}" severity="alert" >
<p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu" action="#{form.retirerPersonne}" oncomplete="confirmation.hide()"/>
<p:commandButton value="#{msg['form.supprimer.non']}" onclick="confirmation.hide()" type="button" />
</p:confirmDialog>
<!-- dataTable-->
<p:dataTable id="personnes" value="#{form.personnes}" var="personne" emptyMessage="#{msg['form.noData']}"
selection="#{form.personneChoisie}" selectionMode="single">
...
</p:dataTable>
</ui:define>
</ui:composition>
</html>
- 第 9–11 行:为第 21 行(id 属性)中的 dataTable 定义一个上下文菜单(for 属性)。当您右键单击“人员”表时,该上下文菜单会显示出来,
- 第 10 行:我们的菜单仅包含一个选项(menuItem 标签)。当点击此选项时,onclick 属性中的 JavaScript 代码将被执行。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 调用后,可以从服务器接收响应。为此,我们使用了 AJAX 调用的 oncomplete 属性:
![]() |
[index.xhtml] 表单的更改如下:
...
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
...
<!-- dialog box 1 -->
<p:confirmDialog widgetVar="confirmation" ... >
<p:commandButton value="#{msg['form.supprimer.oui']}" update=":formulaire:contenu" action="#{form.retirerPersonne}" oncomplete="handleRequest(xhr, status, args);confirmation.hide()"/>
<p:commandButton ... />
</p:confirmDialog>
<!-- Javascript -->
<script type="text/javascript">
function handleRequest(xhr, status, args) {
// erreur ?
if(args.msgErreur) {
alert(args.msgErreur);
}
}
</script>
...
</p:dataTable>
</ui:define>
</ui:composition>
</html>
- 第 7 行:oncomplete 属性调用第 13–18 行中的 JavaScript 函数,
- 第 13 行:方法签名必须是这个。args 是一个字典,服务器端模型可以为其赋值,
- 第 15 行:我们检查 args 字典中是否存在名为 'msgError' 的属性。如果存在,则将其显示(第 16 行)。
在模型中,[removePerson] 方法的实现如下:
public void retirerPersonne() {
// suppression aléatoire
int i = (int) (Math.random() * 2);
if (i == 0) {
// on enlève la personne choisie
personnes.remove(personneChoisie);
} else {
// on renvoie une erreur
String msgErreur = Messages.getMessage(null, "form.msgErreur", null).getSummary();
RequestContext.getCurrentInstance().addCallbackParam("msgErreur", msgErreur);
}
}
- 第 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 行:一个复杂的语句,它将名为 'msgError' 的属性添加到我们之前提到的 args 字典中,其值由第 9 行构建的 msgError 提供。随后,[index.xhtml] 中的 JavaScript 方法会检索该属性:
<!-- Javascript -->
<script type="text/javascript">
function handleRequest(xhr, status, args) {
// erreur ?
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">
<!-- title -->
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<!-- toolbar-->
<p:toolbar>
<p:toolbarGroup align="left">
...
</p:toolbarGroup>
<p:toolbarGroup align="right">
...
</p:toolbarGroup>
</p:toolbar>
</ui:define>
</ui:composition>
</html>
- 第 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。您可以在以下网址 [http://jqueryui.com/themeroller/] [6] 找到图标列表:
![]() |
- 要查找图标名称,只需将鼠标悬停在其上方。该名称随后将用于 <commandButton> 组件的 icon 属性中,例如 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 {
// model
private List<Personne> personnes;
private int personneId;
// manufacturer
public Form() {
// initialization of the list of persons
personnes = new ArrayList<Personne>();
personnes.add(new Personne(1, "dupont", "jacques"));
personnes.add(new Personne(2, "durand", "élise"));
personnes.add(new Personne(3, "martin", "jacqueline"));
}
public void supprimerPersonne() {
// search for the selected person
int i = 0;
for (Personne personne : personnes) {
// current person = selected person?
if (personne.getId() == personneId) {
// delete the current person from the list
personnes.remove(i);
// we're done
break;
} else {
// next person
i++;
}
}
}
// getters and setters
...
}
工具栏右侧的组件如下:
<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. 结论
现在我们已经掌握了足够的知识,可以将示例应用程序移植到 PrimeFaces 上。我们仅介绍了大约十五个组件,而该库中包含的组件超过 100 个。建议读者直接在 PrimeFaces 网站上搜索任何缺失的组件。
5.20. 使用 Eclipse 进行测试
这些 Maven 项目可在示例网站 [1] 上获取:
![]() |
导入 Eclipse 后,即可运行 [2]。在 [3] 中选择 Tomcat。随后,项目内容将显示在 Eclipse 的内置浏览器中 [3]。
![]() |



















































