web.xml.jsf
这次,我想与您分享我最近从JSF 2.2功能中学到的知识。 为此,我决定创建一个简单的ajax,可滚动,延迟加载的数据表。
请注意, 绝不这是相当大的库如Primefaces , RichFaces的或ICEFaces的 。 这只是为了告诉您我学到了什么。 (当然,如果您愿意,没有人可以阻止您使用它)。
这个概念
请允许我先告诉您这个概念,或者如果您愿意的话,请告诉您这种情况。 实际上非常简单。
我们有一个Customer
实体列表,基本上,我们将实现一个数据表,该表是可滚动的,可以延迟加载列表并使用ajax。
注意:我假设您对JPA和EJB感到满意,因此尽管我可能在这里和那里提到它们,但在本文中不再讨论。
使用模板
就像任何JSF应用程序一样,我也从使用模板开始。 将模板与Netbeans一起使用非常容易。 只需选择选项Facelets模板,然后选择您的布局即可。
注意,Netbeans还会生成两个样式表cssLayout.css
和default.css
。 还要注意,在新生成的模板中,它们是使用文件位置定位的,如下所示:
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="./../resources/css/default.css" rel="stylesheet" type="text/css" />
<link href="./../resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
<title>Facelets Template</title>
</h:head>
我对默认模板做了很多修改。
首先,启动应用程序服务器时遇到一个问题,即样式表未正确加载。 因此,为了解决此问题,我使用了JSF Resource Locator (Lubke 2008)。
我还创建了每个子页面,即页眉,菜单和页脚。 如果未指定,则它们充当默认值 。
因此,我的页面集合如下:
template.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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<h:outputStylesheet library="css" name="default.css" />
<h:outputStylesheet library="css" name="cssLayout.css" />
<title>Facelets Template</title>
</h:head>
<h:body>
<div id="top">
<ui:insert name="header">
<ui:include src="header.xhtml" />
</ui:insert>
</div>
<div>
<div id="left">
<ui:insert name="menu">
<ui:include src="menu.xhtml" />
</ui:insert>
</div>
<div id="content" class="left_content">
<ui:insert name="content">
<!-- empty content goes here -->
</ui:insert>
</div>
</div>
<div id="bottom">
<ui:insert name="bottom">
<ui:include src="footer.xhtml" />
</ui:insert>
</div>
</h:body>
</html>
注意,不是使用文件定位器./../resources/css/default.css
,而是使用h:outputStylesheet
。 使用这种方法,只需将所有资源放在resources
目录中,然后通过属性library
指向包含您的资源的文件夹来指向它。 因此,例如<h:outputStylesheet library="css" name="default.css" />
确实是在resources/css/default.css
位置查看文件。
header.xhtml,footer.xhtml,menu.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:ui="http://java.sun.com/jsf/facelets">
<body>
<ui:composition>
<!--
I created each file for header, footer and menu, but I simply list
them here just to show you
-->
<h1>Default Header</h1>
<!-- h1>Default Menu</h1 -->
<!-- h1>Default Footer</h1 -->
</ui:composition>
</body>
</html>
接下来,使用模板。 因此,我们创建一个名为customers.xhtml
的文件来使用它。
customer.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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<ui:composition template="./common/template.xhtml">
<ui:define name="content">
<h:outputText value="List of customers goes here!!!" />
</ui:define>
</ui:composition>
</body>
</html>
然后,您可以运行应用程序服务器并通过http://<the path to your application>/customers.jsf
访问该页面。 输出应如下图所示。
添加设施以创建客户
首先,在模板中,让我们添加一个工具以在customers.xhtml中添加customers.xhtml
。 我们可以简单地使用JSF ManagedBean
来做到这一点。 但是,Core Javaserver Faces第3建议:
对于可以在JSF页面中使用的bean,有两种单独的机制(CDI bean和JSF受管bean)是历史上的意外。 我们建议您使用CDI bean,除非您的应用程序必须在诸如Tomcat之类的普通servlet运行程序上运行。
(Geary,Horstmann,2010年)
按照建议,从现在开始,我将使用CDI bean。
在实际的应用程序中,我确信还有很多数据要捕获。 但是,在此示例中,让我们仅捕获客户的姓名。
基本上,我们需要一个Model
和一个Controller
。 因此,让我们首先创建这些类。
CustomerData.java
package id.co.dwuysan.customer;
import javax.enterprise.inject.Model;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Model
public class CustomerData {
@NotNull
@Size(min = 1, max = 50)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
void clear() {
setName(null);
}
}
CustomerData.java
package id.co.dwuysan.customer;
import id.co.dwuysan.service.CustomerService;
import javax.ejb.EJBException;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.ActionEvent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
@Named
public class CustomerRegistationAction {
@Inject
private CustomerData customer;
@Inject
private CustomerService customerService;
public void register(ActionEvent event) {
try {
// calls EJB and do registration here
} catch (EJBException e) {
FacesContext.getCurrentInstance().addMessage(event.getComponent().getClientId(), new FacesMessage(e.getMessage()));
throw new AbortProcessingException(e.getMessage());
}
this.customer.clear();
}
}
注意,在这种情况下,我们使用@Named
注释。 由于我们没有给它任何范围注释,因此默认为@RequestScoped
(BalusC 2011a)。
下一步是修改我们的customers.xhtml
以使用这些类,以便我们可以注册客户。
customer.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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ez="http://java.sun.com/jsf/composite/ezcomp">
<body>
<ui:composition template="./common/template.xhtml">
<ui:define name="content">
<f:view transient="true">
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="txtCustomerName" value="Customer name" />
<h:inputText id="txtCustomerName" value="#{customerData.name}" />
<h:message for="txtCustomerName" styleClass="error" />
<div />
<h:commandButton id="cmdRegisterCustomer" value="Submit" action="customers" actionListener="#{customerRegistationAction.register}" />
<h:message for="cmdRegisterCustomer" styleClass="error" />
</h:panelGrid>
</h:form>
</f:view>
</ui:define>
</ui:composition>
</body>
</html>
好的,一旦完成,您就可以注册客户。 屏幕应如下所示。
定义“合同”
这部分是真正的交易。 我所说的“合同”不是真正的合同本身。 相反,我指的是我们首先设计Facelet xhtml的方式,这将驱动实际的后备bean类。
因此,让我们首先概述一下需求:
- 记录应在表中分页,并从数据库中延迟加载
- 用户应该能够选择一个页面中显示多少条记录
- 用户应该能够看到当前页面是什么,页面总数和记录
- 用户应该能够滚动浏览下一个和上一个记录
- 使用AJAX
在一个应用程序中,必然会有很多清单。 因此,向前迈进的最好方法是创建一个易于重用的组件。 由于组件本身由许多其他组件组成,因此我相信此功能正式称为复合组件 。 在JSF 1.x时代,这非常痛苦。 开发人员需要编写许多类/文件。 使用JSF 2.x,这非常容易(我必须说,这非常优雅)。
让我们从创建组件开始。
创建复合组件
我将这个组件命名为paginator
。
我相信您会找到很多关于如何执行此操作的解释,因此,我将跳过“说明”部分,仅向您展示我的操作。
我在resources/ezcomp
目录下创建了一个文件paginator.xhtml
。 因此,稍后我将使用命名空间xmlns:ez="http://java.sun.com/jsf/composite/ezcomp
来引用此组件。
让我们继续进行paginator.xhtml
的实际实现。 请参考以下资源,然后我将逐步指导您。
paginator.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:cc="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="paginateModel" /> <!-- 01 -->
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<!-- 02 -->
<h:inputHidden value="#{cc.attrs.paginateModel.sortField}" />
<h:inputHidden value="#{cc.attrs.paginateModel.ascending}" />
<h:inputHidden value="#{cc.attrs.paginateModel.page}" />
<!-- 03 -->
<h:panelGrid columns="2" cellpadding="5" cellspacing="5">
<h:outputLabel value="Rows-per-page" />
<h:selectOneRadio value="#{cc.attrs.paginateModel.rowsPerPage}">
<f:selectItem itemValue="5" itemLabel="5" />
<f:selectItem itemValue="10" itemLabel="10" />
<f:selectItem itemValue="20" itemLabel="20" />
<f:selectItem itemValue="100" itemLabel="100" />
<f:ajax execute="@form" render="@form" listener="#{cc.attrs.paginateModel.updateRowsPerPage}" />
</h:selectOneRadio>
</h:panelGrid>
<!-- pagination -->
<h:panelGrid columns="3" cellpadding="5" cellspacing="5">
<!-- 04 -->
<h:commandLink value="<<" actionListener="#{cc.attrs.paginateModel.navigatePage(false)}" style="display: #{cc.attrs.paginateModel.page gt 1 ? 'block' : 'none'}">
<f:ajax execute="@form" render="@form" />
</h:commandLink>
<!-- 05 -->
<h:outputLabel value="#{cc.attrs.paginateModel.page} ⁄ #{cc.attrs.paginateModel.totalPages} " />
<!-- 06 -->
<h:commandLink value=">>" actionListener="#{cc.attrs.paginateModel.navigatePage(true)}" style="display: #{cc.attrs.paginateModel.page lt cc.attrs.paginateModel.totalPages ? 'block' : 'none'}">
<f:ajax execute="@form" render="@form" />
</h:commandLink>
</h:panelGrid>
<cc:insertChildren /><!-- 07 -->
<br />
<!-- 08 -->
<h:outputFormat value="There are {0} record(s).">
<f:param value="#{cc.attrs.paginateModel.recordCount}" />
</h:outputFormat>
</cc:implementation>
</html>
下一节将说明我在customers.xhtml
提出的每个编号点。
01. cc:attribute
和paginatedModel
这部分是此xhtml中的实现以及此组件的客户端/用户之间的'interface'
。 简而言之,将其视为使用此组件时必须提供的参数。
02.隐藏参数
现在这部分有很多解释。
首先,我需要快一点告诉您我正在使用RequestScoped
bean支持记录列表。 需要注意的另一件事是,我实际上使用了<f:view transient="true">
,这是JSF 2.2中可用的功能,因此使该页面变为无状态。 如果您在浏览器中开源,则状态现在为:
<input type="hidden" name="javax.faces.ViewState" id="j_id1:javax.faces.ViewState:0" value="stateless" autocomplete="off" />
由于此页面是无状态的,因此您需要在两次调用之间管理自己的状态(Riem 2013)。 因此使用隐藏参数。 想象一下,假设您正在显示100页中的第4页,并且用户决定导航到下一页,因为它是无状态的,所以您需要知道当前是哪一页,无论当前显示是升序还是降序以及目前的排序方式。
03.每页行数选择
这一部分很不言自明,只有一部分。 基本上,我们列出了供用户选择的选项,以供选择以每页显示多少条记录。 当用户选择时,我们需要使用AJAX重新执行查询并更新页面。 因此,本节的使用:
<f:ajax execute="@form" render="@form" listener="#{cc.attrs.paginateModel.updateRowsPerPage}" />
本部分的内容基本上是基于用户选择,执行方法updateRowsPerPage
并更新此form
。 如第02节所述, 'execute'
还意味着我们正在考虑这种form
所有参数。
04/06。 导航“后退”和“前进”
我将在本节中一起解释04和06部分,因为它们非常相似。
让我们从第04部分开始。这基本上将呈现一个链接,以允许用户浏览'Back'
。 仅在当前页面不是第一页的情况下,我才简单地使用display
属性来进行条件渲染(否则,将无法返回第一页,对吗?)。 第6部分非常相似,但条件是仅在当前页面不是最后一页时才呈现。
我要说明的下一部分是单击该页面时的actionListener
。 基本上,我们现在知道由于h:inputHidden
(参见02节),当前页面是什么。 因此,使用04和06部分中的两个命令链接,我们需要标记这是哪种导航。 为此,我只使用布尔值。
最后,因为我们想使用AJAX进行导航,所以我们还需要使用<f:ajax>
。 同样,与前面的部分有关在@form
上execute
和render
原因相同。
05当前页和总页数
这部分只是呈现当前页面和可用页面总数。 不言自明
06见第04节
见第04节
07 insertChildren
尽管我们可以重复使用分页器,但我认为创建一个将数据模型自动呈现到表中的组件非常困难。 至少,页面设计者需要确定要呈现的列(我确信必须有一种神奇的方式以表格方式自动呈现所有内容。也许Oracle ADF Faces会为您做到这一点。无论如何,我不必费心找到它)。
使用此标记时,基本上稍后使用<ezcomp:paginator>...</ezcomp:paginator>
,介于两者之间的内容都将在第07部分呈现。请像<ui:decorate>
那样考虑一下。
08显示总记录
对于此部分,我们只显示总记录。 不言自明。
使用复合组件
现在,我们基本上需要使用此复合组件。 因此,我们需要如下修改我们的customers.xhtml
:
customer.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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ez="http://java.sun.com/jsf/composite/ezcomp"> <!-- 01 -->
<body>
<ui:composition template="./common/template.xhtml">
<ui:define name="content">
<f:view transient="true">
<h:form id="frmCreateCustomer">
<h:panelGrid columns="3">
<h:outputLabel for="txtCustomerName" value="Customer name" />
<h:inputText id="txtCustomerName" value="#{customerData.name}" />
<h:message for="txtCustomerName" styleClass="error" />
<div />
<h:commandButton id="cmdRegisterCustomer" value="Submit" action="customers" actionListener="#{customerRegistationAction.register}">
<f:actionListener binding="#{listCustomers.sortByDefault}" />
<f:ajax execute="@form" render="@form :frmListCustomers" /> <!-- 03 -->
</h:commandButton>
<h:message for="cmdRegisterCustomer" styleClass="error" />
</h:panelGrid>
</h:form>
<br />
<h:form id="frmListCustomers"> <!-- 02 -->
<ez:paginator paginateModel="#{listCustomers}"> <!-- 04 -->
<h:dataTable value="#{listCustomers.data}" var="cust"> <!-- 05 -->
<h:column>
<f:facet name="header">
<h:commandLink value="Name" actionListener="#{listCustomers.sort('name')}">
<f:ajax execute="@form" render="@form" /> <!-- 06-->
</h:commandLink>
</f:facet>
<h:outputText value="#{cust.name}" />
</h:column>
</h:dataTable>
</ez:paginator>
</h:form>
</f:view>
</ui:define>
</ui:composition>
</body>
</html>
同样,我将一步一步地指导您:
01命名空间
如前所述,由于我们将分页器放置在resources/ezcomp/paginator.xhtml
,因此JSF允许我们使用名称空间http://java.sun.com/jsf/composite/ezcomp
来引用它。 因此,稍后(如04所示),您可以通过使用ezcomp:paginator
来使用新创建的ezcomp:paginator
。
02列表表格以自己的形式
让我们跳到第二部分。第二部分显示在此customers.xhtml
,我们使用两种形式。 主要是因为我要与表进行交互,所以您不希望它影响或受另一种形式的影响,在本例中为“创建客户”部分。
让我们说,假设我们只对整个customers.xhtml
使用一种形式。 单击按钮进行排序(第06部分)时,可能会触发对创建文本框的验证,例如,客户名称不能为空。 我们不想要那个。
由于第03节中概述的原因,我们还需要给此表单起一个名字。
03重新呈现其他表格
这里的意图是每当添加新客户时便能够刷新表。 注意,命名容器(相对于02节)是第二个h:form
。 因此,由于“创建客户”和表位于不同的命名容器中,因此让我们只刷新整个第二个表单,这是通过使用正确的“搜索表达式”完成的。 首先使用“:”,这意味着JSF将从文档的根目录搜索树(BalusC,2011b)。
04使用我们新创建的复合组件
这就是使用我们新创建的paginator
组件的简单方法,即,将适当的参数传递给它(此后)。
另请参阅paginator.xhtml
及其说明。
05实际数据表
这是实际的数据表。 如前所述,尽管我们可以创建通用分页器,但我认为创建通用表渲染将非常困难。 因此,在这种情况下,我只想向您展示确定要渲染的列的情况。
需要注意的一件事是,我们调用listCustomers.data
为数据表提供javax.faces.DataModel
。 基于页面,大小,排序等,此“数据”应该是正确的数据。如果您仍然不明白,请不要担心。 在下一节中,它将变得更加清晰。
06排序
根据上述要求,我们需要实现排序。 这是不言自明的,使用f:ajax
与前面提到的原因相同。
创建类以支持分页
好的,我们已经使用paginator.xhtml
创建了“合同/需求”,然后在customers.xhtml
上使用了它。 现在,我们需要实现一个支持该功能的类。
我要做的是遍历customers.xhtml
和paginator.xhtml
。 您也可以这样做,以便您可以轻松理解以下列出的每个要点:
- 首先,查看
customers.xhtml
,该类称为“ listCustomers”。 由于我们正在使用CDI,因此需要使用@Named
将bean暴露给JSF环境。 我们可以创建ListCustomers.java
,或在@Named
批注中填充value
。 - 过去,我将
@ViewScoped
用于此bean。 但是,由于我们将JSF的“无状态”功能与f:view transient="true"
,因此我们无法再使用@ViewScoped
。 这些bean与JSF框架所管理的视图相关。 但是,通过此无状态功能,视图始终会被重新创建,并且该范围内的Bean也会被重新创建(Busscher 2013)。 因此,最好使用的范围是@RequestScoped
。 - 它需要具有返回
javax.faces.DataModel<T>
getData()
。 这应该返回要显示的Customer
列表。 - 它需要支持
public void sort(final String sortField)
以支持标头排序。 -
String getSortField()
,boolean isAscending()
String getSortField()
,int getPage()
以及它们相应的int getPage()
器/设置器的访问器。 这些是必需的,与我们的隐藏输入有关。 -
rowsPerPage
访问器/rowsPerPage
器,以支持我们的每页行数选择。 - 它需要支持
public void updateRowsPerPage(AjaxBehaviorEvent event)
,以便我们可以基于用户的每页行数选择来更新列表。 - 它需要支持
public void navigatePage(final boolean forward)
。 - 记录
recordCount
无论如何,您现在应该可以实现该类。
但是,我实现它的方法是将其分为两部分。 如果您考虑一下,让我们说以后您的应用程序中有多个列表,例如订单列表,人员列表,发票等。 它们将以相同的方式实现。 我认为最好先创建一个基类,然后再用实际的实体对其进行扩展。
首先是基层。
DataListingSupport.java
package id.co.dwuysan.util;
import java.io.Serializable;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.model.DataModel;
public abstract class DataListingSupport<T extends Serializable> implements Serializable {
private int recordCount = 0;
private int totalPages = 0;
private DataModel<T> data;
private int page = 1;
private Integer rowsPerPage = null;
private boolean ascending = true;
private String sortField;
public void navigatePage(final boolean forward) {
setPage((forward) ? ++page : --page);
refresh();
}
public void sort(final String sortField) {
setSortField(sortField);
setAscending(getSortField().equals(sortField) ? !isAscending() : true);
refresh();
}
public void updateRowsPerPage(final AjaxBehaviorEvent event) {
setPage(1); // page must reset to the first one
refresh();
}
public void refresh() {
// hook to populate count and data
populateCountAndData();
// compute total pages
setTotalPages(countTotalPages(getRecordCount(), getRowsPerPage()));
}
/**
* The concreate implementation of this class must perform data retrieval
* based on the current information available (accessible via methods such
* as {@link #getSortField()}, {@link #isAscending()}, etc.
* <p>
* The implementation is responsible in populating the values for {@link #setRecordCount(int)}
* and {@link #setData(javax.faces.model.DataModel)}
*/
protected abstract void populateCountAndData();
/************************************************************** HELPER(S) */
private static int countTotalPages(int totalRecord, int rowsPerPage) {
int pageCounter = 0;
for (int pageCountTracker = 0; pageCountTracker < totalRecord; ++pageCounter) {
pageCountTracker += rowsPerPage;
}
return pageCounter;
}
/************************************************* ACCESSORS AND MUTATORS */
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
public boolean isAscending() {
return ascending;
}
public void setAscending(boolean ascending) {
this.ascending = ascending;
}
public Integer getRowsPerPage() {
return rowsPerPage;
}
public void setRowsPerPage(Integer rowsPerPage) {
this.rowsPerPage = rowsPerPage;
}
public DataModel<T> getData() {
return data;
}
public void setData(DataModel<T> data) {
this.data = data;
}
public String getSortField() {
return sortField;
}
public void setSortField(String sortField) {
this.sortField = sortField;
}
public int getRecordCount() {
return recordCount;
}
public void setRecordCount(int recordCount) {
this.recordCount = recordCount;
}
public int getTotalPages() {
return totalPages;
}
public void setTotalPages(int totalPages) {
this.totalPages = totalPages;
}
}
接下来是我们如何扩展该类以创建支持bean以支持Customer
列表。
DataListingSupport.java
package id.co.dwuysan.customer;
import id.co.dwuysan.entity.Customer;
import id.co.dwuysan.service.CustomerService;
import id.co.dwuysan.util.DataListingSupport;
import javax.enterprise.context.RequestScoped;
import javax.faces.model.ListDataModel;
import javax.inject.Inject;
import javax.inject.Named;
@RequestScoped
@Named
public class ListCustomers extends DataListingSupport<Customer> {
@Inject
private CustomerService customerService;
public ListCustomers() {
setSortField("name");
setRowsPerPage(10);
}
@Override
protected void populateCountAndData() {
/*
* This is where we call an EJB (or whatever service layer you have)
* to perform data retrieval.
*
* You need to make sure to retrieve the result (paginated, sorted), and
* also the total number of records.
*/
setRecordCount(result.getCount());
setData(new ListDataModel<>(result.getResult()));
}
}
因此,您看到,使用这种方法,如果我需要另一种清单,即订单清单,我们可以轻松扩展DataListingSupport<>
。
结果
在向您展示全部结果之前,我们可能要添加的一件事是在页面显示时从数据库加载所有当前客户的操作。 JSF 2.2添加了一项称为“查看操作”的功能,该功能使此操作非常容易(McGinn 2011)。
所有你需要做的,是添加这个f:viewAction
后f:view
如下:
customer.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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ez="http://java.sun.com/jsf/composite/ezcomp"> <!-- 01 -->
<body>
<ui:composition template="./common/template.xhtml">
<ui:define name="content">
<f:view transient="true">
<!-- to load on display of the page -->
<f:metadata>
<f:viewAction transient="true" action="#{listCustomers.refresh}"/>
</f:metadata>
<h:form id="frmCreateCustomer">
<!-- the rest of the code remains unchanged -->
</h:form>
</f:view>
</ui:define>
</ui:composition>
</body>
</html>
注意,我们只是简单地调用DataListingSupport<>#refresh()
。 viewAction
实际上分析String返回以执行隐式导航。 在这种情况下,我们的refresh()
方法实际上返回void
,因此不执行导航(McGinn 2011)。
结果应该是这样的:
我从纳斯达克100指数中挑选了客户名单。
因此,现在您有了一个可滚动,可延迟加载的AJAX数据表。
参考资料
- BalusC,2011a, “ netbeans中的JSF 2应用程序中默认的Managed Bean Scope是什么?” ,堆栈溢出,2013年11月6日访问
- BalusC,2011b, “如何在JSF ajax中引用组件? 在 2013年11月11日访问的堆栈溢出中, 在视图中找不到标识符为“ foo”的组件
- Busscher,R,2013年, “解释了JSF 2.2无状态视图” ,JSF Corner,2013年11月12日访问
- Geary,D,Horstmann,C,2010年,“ Core JavaServer Faces(第三版)”,第三版,美国加利福尼亚州Prentice Hall。
- Lubke,R,2008, “ JSF 2.0新功能预览系列(第4部分)资源重定位” ,Oracle.com,2013年10月7日访问
- McGinn,T,2011年, “新的JavaServer Faces 2.2功能:viewAction组件” ,Oracle.com,2013年11月17日访问
- Oracle,2013年, “复合组件” ,Oracle.com,2013年11月7日访问
- 里姆,男,2013年, “ JSF技巧#26 – JSF变得无状态” ,Java.net,2013年11月9日访问
web.xml.jsf