Instead of creating a long page to display a large number of tabular items, you want to display a limited fraction of the data on a page, allowing the user to navigate between the pages.
The simplest way to perform paging without resorting to a third-party
tag library is to leverage the arithmetic capabilities of JSTL EL and
the features of JSTL's c:forEach
tag. The JSP page
(paged_data.jsp) of Example 4-10
presents a complete page that supports paging through a Collection.
Example 4-10. Using JSTL for data paging
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix= "bean" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head> <title>Struts Cookbook - Chapter 4 : Paging</title> </head> <body> <jsp:useBean id="pagedData" class="com.oreilly.strutsckbk.ch04. PagedData"/> <bean:size id="listSize" name="pagedData" property="data"/> <c:set var="pageSize" value="10"/> <c:set var="pageBegin" value="${param.pageBegin}"/> <c:set var="pageEnd" value="${pageBegin + pageSize - 1}"/> <c:if test="${(pageBegin - pageSize) ge 0}"> <a href='<c:url value="paged_data.jsp"> <c:param name="pageBegin" value="${pageBegin - pageSize}"/> </c:url>'> Prev </a> </c:if> <c:if test="${(listSize gt pageSize) and (pageEnd lt listSize)}"> <a href='<c:url value="paged_data.jsp"> <c:param name="pageBegin" value="${pageBegin + pageSize}"/> </c:url>'> Next </a> </c:if> <table border="2"> <tr> <th>First Name</th> <th>Last Name</th> <th>Term of Office</th> </tr> <c:forEach var="pres" items="${pagedData.data}" begin="${pageBegin}" end="${pageEnd}"> <tr> <td> <c:out value="${pres.firstName}"/> </td> <td> <c:out value="${pres.lastName}"/> </td> <td> <c:out value="${pres.term}"/> </td> </tr> </c:forEach> </table> </body> </html>
An application's usability improves when the user doesn't have to scroll around, vertically or horizontally, to see all the data on a web page. For a business application, this usability, or lack thereof, can make a measurable difference in productivity. This recipe addresses the problem of displaying a collection of data of indeterminate length. This frequently occurs in database-driven applications where the data is rendered in tabular fashion.
The Solution provided can be completely implemented at the
presentation level. No requirements are made on the model of the data
or on any controller Action
s that process the
data.
Warning
This solution provides presentation-level paging. It doesn't restrict the data amount initially received from the underlying persistent store.
In the Solution, the data being paged through is hardcoded into the JavaBeans shown in Example 4-11. In a real application, these data would more likely come from a persistence store such as a database or filesystem.
Example 4-11. JavaBean that holds data collection
package com.oreilly.strutsckbk.ch04; import java.util.ArrayList; import java.util.List; public class PagedData { private List list; public PagedData( ) { list = new ArrayList( ); list.add( new President( "Washington", "George", "1789-97") ); list.add( new President( "Adams", "John", "1797-1801") ); list.add( new President( "Jefferson", "Thomas", "1801-09") ); list.add( new President( "Madison", "James", "1809-17") ); list.add( new President( "Monroe", "James", "1817-25") ); list.add( new President( "Jackson", "Andrew", "1829-37") ); list.add( new President( "Van Buren", "Martin", "1837-41") ); list.add( new President( "Harrison", "William Henry", "1841") ); list.add( new President( "Tyler", "John", "1841-45") ); list.add( new President( "Polk", "James", "1845-49") ); list.add( new President( "Taylor", "Zachary", "1849-50") ); list.add( new President( "Fillmore", "Millard", "1850-53") ); list.add( new President( "Pierce", "Franklin", "1853-57") ); list.add( new President( "Buchanan", "James", "1857") ); list.add( new President( "Lincoln", "Abraham", "1861-65") ); list.add( new President( "Johnson", "Andrew", "1865-69") ); list.add( new President( "Grant", "Ulysses S.", "1869-77") ); list.add( new President( "Hayes", "Rutherford B.", "1877-81") ); list.add( new President( "Garfield", "James", "1881") ); list.add( new President( "Arthur", "Chester", "1881-85") ); list.add( new President( "Cleveland", "Grover", "1885-89") ); list.add( new President( "Harrison", "Benjamin", "1889-93") ); list.add( new President( "Cleveland", "Grover", "1893-97") ); list.add( new President( "McKinley", "William", "1897-1901") ); list.add( new President( "Roosevelt", "Theodore", "1901-09") ); list.add( new President( "Taft", "William H.", "1909-13") ); list.add( new President( "Wilson", "Woodrow", "1913-21") ); list.add( new President( "Jackson", "Andrew", "1829-37") ); list.add( new President( "Harding", "Warren", "1921-23") ); list.add( new President( "Coolidge", "Calvin", "1923-29") ); list.add( new President( "Hoover", "Herbert", "1929-33") ); list.add( new President( "Roosevelt", "Franklin D.", "1933-45") ); list.add( new President( "Truman", "Harry", "1945-53") ); list.add( new President( "Eisenhower", "Dwight", "1953-61") ); list.add( new President( "Kennedy", "John F.", "1961-63") ); list.add( new President( "Johnson", "Lyndon", "1963-69") ); list.add( new President( "Nixon", "Richard", "1969-74") ); list.add( new President( "Ford", "Gerald", "1974-77") ); list.add( new President( "Carter", "Jimmy", "1977-81") ); list.add( new President( "Reagan", "Ronald", "1981-89") ); list.add( new President( "Bush", "George H.W.", "1989-93") ); list.add( new President( "Clinton", "William J.", "1993-2001") ); list.add( new President( "Bush", "George W.", "2001-present") ); } public List getData( ) { return list; } }
The President
class encapsulates information about
a president, as shown in Example 4-12.
Example 4-12. Value object representing a President
package com.oreilly.strutsckbk.ch04; public class President { public President(String lname, String fname, String term) { lastName = lname; firstName = fname; this.term = term; } public String getFirstName( ) { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName( ) { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getTerm( ) { return term; } public void setTerm(String term) { this.term = term; } private String lastName; private String firstName; private String term; }
The JSP page (Example 4-10) contains all the logic
that handles paging. Though this JSP could have been implemented
using only Struts tags, it would have required a substantial amount
of scripting to perform the necessary arithmetic. A better approach
is to use JSTL. JSTL easily supports arithmetic calculations in
expressions; in addition, the JSTL c:forEach
tag
is designed to allow for specification of beginning and ending
indices for the collection being iterated. This ability is a natural
fit for the requirements of paging.
Paging requires you to track the current page that the user is on.
When the page is initially displayed, you want to display the first
n rows, where n is the
desired page size. In the Solution, this
value—pageSize
—is set to 10:
<c:set var="pageSize" value="10"/>
The JSP page is designed to accept a request parameter that
identifies the beginning index for the current page:
pageBegin
. The ending index,
pageEnd
, is calculated by adding the page size to
the value of the beginning index. The calculation subtracts 1 from
this result because the indices in a c:forEach
loop are zero-based.
<c:set var="pageEnd" value="${pageBegin + pageSize - 1}"/>
The JSP page is designed to generate links for the previous and next pages. The link loops back to the current page with the URL query string containing the calculated beginning index. The beginning index for the link to the previous page is calculated as follows:
pageBegin - pageSize
The beginning index for the link to the next page is likewise calculated:
pageBegin + pageSize
Additionally, you don't want the links to display if
they aren't valid. In other words, you
don't want a link to the previous page when you are
displaying the first page. Similarly, there
shouldn't be a link to the next page when the last
page is displayed. For this requirement, the JSP page needs to know
the total size—essentially the number of rows—in the
list. This value is determined using the Struts
bean:size
tag:
<bean:size id="listSize" name="pagedData" property="data"/>
The JSTL c:url
tag is used to generate the
href
attribute for the HTML links:
<a href='<c:url value="paged_data.jsp"> <c:param name="pageBegin" value="${pageBegin - pageSize}"/> </c:url>'> Prev </a>
This results in a link rendered something like the following:
<a href='paged_data.jsp?pageBegin=20'> Prev </a>
Though the Struts html:link
tag could have been
used, it doesn't add any significant advantage over
using the JSTL c:url
tag.
Tip
A good practice to use when building JSP pages is to minimize mixing tag libraries when it doesn't add to the overall functionality of the page.
If you examine the Solution, you may notice what appears to be a
defect: the ending index is calculated by adding the beginning index
to the page size. With the examples, the list being iterated has over
42 elements—e.g., the number of U.S. presidents to date. The
first page displays elements 0-9, the second displays 10-19, etc. For
the last page, begin
will be set to
40
and end
will be set to
49
. Yet only 42 elements are in the list:
end
should be set to 41
. Fear
not, for JSTL is smart enough not to overrun the list. When the
c:forEach
loop reaches the end of the list, it
gracefully discontinues iteration regardless of the value of the
end
attribute.
Now take a look at the resulting web pages. First, Figure 4-6 shows the rendered web page when the JSP page is initially accessed.
Figure 4-7 shows the web page after paging forward two pages to display the third page (the 31st through 40th presidents).
Though the Solution presented satisfies many application requirements, if the size of the data to be displayed is a large result set from a database and the number of rows could be significantly higher—say in the thousands—then you'll need to consider additional alternatives. Look to your underlying persistence mechanism for solutions that manage large data sets at the persistence level.
The Pager Tag Library is a popular JSP tag library that supports paging. It provides a presentation that can emulate the paging style of search engines such as Google and Alta Vista. Information on this library can be found at http://jsptags.com/tags/navigation/pager/index.jsp.
The display tag library is an open source JSP tag library that is well suited for the display of tabular data. It supports paging in various manners and is discussed in Recipe 4.6.
Get Jakarta Struts Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.