初学JSF的大概都会遇到这么个问题,从Bean中将数据读取出来以后,用DataTable在页面上显示。是很常用的数据内容输出的方法,但是DataTable本身没有翻页的功能。如何给DataTable增加翻页呢?这里分享一下CoreJSF 3rd Edition中的pager demo来完成这个数据分页。其思路可以扩展到各种大量数据需要翻页的情况。
不想自己实现这个翻页的朋友,可以使用各种UI Framework中自带的翻页组件。比如Apache Tomahawk 中的 dataScroller 具体实现方法其实和这里要谈到的类似。
OK首先是JSF网页部分:
<?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:ui="http://java.sun.com/jsf/facelets"
xmlns:corejsf="http://corejsf.com">
<h:head>
<h:outputStylesheet library="css" name="styles.css"/>
<title>Pager Test</title>
</h:head>
<h:body>
<ui:debug/>
<h:form>
<h:dataTable id="timezones" value="#{tz.data}" var="row" rows="10">
<h:column>#{row}</h:column>
</h:dataTable>
<corejsf:pager dataTableId="timezones" showpages="20"
selectedStyleClass="currentPage"/>
</h:form>
</h:body>
</html>tz这个bean的data是个String[],其中放了所有的timezone的名称。这里用DataTable将它输出到html页面上。这个表格将会很长,所以我们需要来将其分页化。
TimeZone bean的内容很简单:
package com.corejsf;
import javax.faces.bean.ManagedBean;
// or import javax.inject.Named;
import javax.faces.bean.RequestScoped;
@ManagedBean(name="tz") // or @Named("tz")
@RequestScoped
public class TimeZoneBean {
private String[] data = java.util.TimeZone.getAvailableIDs();
public String[] getData() { return data; }
}好直到这里都没什么,如果你已经留意前面第一个jsf页面,其中的<corejsf:pager dataTableId=”timezones” showpages=”20″
selectedStyleClass=”currentPage”/>部分,实际就是我们这里的重点了,这是一个定制的Renderer,用这个定制的renderer来控制翻页并且切换DataTable的数据显示。
这里我们的这个翻页Renderer叫“com.corejsf.Pager”该类属于javax.faces.Command。
具体实现方式:
package com.corejsf;
import java.io.IOException;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.component.UIData;
import javax.faces.component.UIForm;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.FacesRenderer;
import javax.faces.render.Renderer;
@FacesRenderer(componentFamily="javax.faces.Command",
rendererType="com.corejsf.Pager")
public class PagerRenderer extends Renderer {
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
String id = component.getClientId(context);
UIComponent parent = component;
while (!(parent instanceof UIForm)) parent = parent.getParent();
String formId = parent.getClientId(context);
ResponseWriter writer = context.getResponseWriter();
String styleClass = (String) component.getAttributes().get("styleClass");
String selectedStyleClass
= (String) component.getAttributes().get("selectedStyleClass");
String dataTableId = (String) component.getAttributes().get("dataTableId");
int showpages = toInt(component.getAttributes().get("showpages"));
// find the component with the given ID
UIData data = (UIData) component.findComponent(dataTableId);
int first = data.getFirst();
int itemcount = data.getRowCount();
int pagesize = data.getRows();
if (pagesize <= 0) pagesize = itemcount;
int pages = itemcount / pagesize;
if (itemcount % pagesize != 0) pages++;
int currentPage = first / pagesize;
if (first >= itemcount - pagesize) currentPage = pages - 1;
int startPage = 0;
int endPage = pages;
if (showpages > 0) {
startPage = (currentPage / showpages) * showpages;
endPage = Math.min(startPage + showpages, pages);
}
if (currentPage > 0)
writeLink(writer, component, formId, id, "<", styleClass);
if (startPage > 0)
writeLink(writer, component, formId, id, "<<", styleClass);
for (int i = startPage; i < endPage; i++) {
writeLink(writer, component, formId, id, "" + (i + 1),
i == currentPage ? selectedStyleClass : styleClass);
}
if (endPage < pages)
writeLink(writer, component, formId, id, ">>", styleClass);
if (first < itemcount - pagesize)
writeLink(writer, component, formId, id, ">", styleClass);
// hidden field to hold result
writeHiddenField(writer, component, id);
}
private void writeLink(ResponseWriter writer, UIComponent component,
String formId, String id, String value, String styleClass)
throws IOException {
writer.writeText(" ", null);
writer.startElement("a", component);
writer.writeAttribute("href", "#", null);
writer.writeAttribute("onclick", onclickCode(formId, id, value), null);
if (styleClass != null)
writer.writeAttribute("class", styleClass, "styleClass");
writer.writeText(value, null);
writer.endElement("a");
}
private String onclickCode(String formId, String id, String value) {
return new StringBuilder().append("document.forms['")
.append(formId).append("']['")
.append(id).append("'].value='").append(value).append("'; document.forms['")
.append(formId).append("'].submit(); return false;").toString();
}
private void writeHiddenField(ResponseWriter writer, UIComponent component,
String id) throws IOException {
writer.startElement("input", component);
writer.writeAttribute("type", "hidden", null);
writer.writeAttribute("name", id, null);
writer.endElement("input");
}
public void decode(FacesContext context, UIComponent component) {
String id = component.getClientId(context);
Map<String, String> parameters
= context.getExternalContext().getRequestParameterMap();
String response = (String) parameters.get(id);
if (response == null || response.equals("")) return;
String dataTableId = (String) component.getAttributes().get("dataTableId");
int showpages = toInt(component.getAttributes().get("showpages"));
UIData data = (UIData) component.findComponent(dataTableId);
int first = data.getFirst();
int itemcount = data.getRowCount();
int pagesize = data.getRows();
if (pagesize <= 0) pagesize = itemcount;
if (response.equals("<")) first -= pagesize;
else if (response.equals(">")) first += pagesize;
else if (response.equals("<<")) first -= pagesize * showpages;
else if (response.equals(">>")) first += pagesize * showpages;
else {
int page = Integer.parseInt(response);
first = (page - 1) * pagesize;
}
if (first + pagesize > itemcount) first = itemcount - pagesize;
if (first < 0) first = 0;
data.setFirst(first);
}
private static int toInt(Object value) {
if (value == null) return 0;
if (value instanceof Number) return ((Number) value).intValue();
if (value instanceof String) return Integer.parseInt((String) value);
throw new IllegalArgumentException("Cannot convert " + value);
}
}配置方面还需要:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/corejsf.taglib.xml</param-value>
</context-param>
</web-app><?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facelettaglibary_2_0.xsd">
<namespace>http://corejsf.com</namespace>
<tag>
<tag-name>pager</tag-name>
<component>
<component-type>javax.faces.Command</component-type>
<renderer-type>com.corejsf.Pager</renderer-type>
</component>
</tag>
</facelet-taglib>具体实现如图,代码在tomcat 6.0.29 x64测试通过。


