Pagination
July 13, 2008
Yojava provides a range of classes and tags to help with pagination and sorting. They have the benefit of being MVC-friendly.
Installing
There are two jars: yojava-common.jar and yojava-common-web.jar (for tags). In future they will be combined into one. Third party dependencies are detailed in the javadoc.
Pagination
First of all, in the controller we might have something like
import org.yojava.common.search.*;
...
Paginator paginator =
new Paginator(maxResultsPerPage, currentPage);
List results = search.find(criteria,
paginator.getIndexOfFirst(), maxResultsPerPage
paginator.setNumResults(search.getTotalNumResults());
/* Add paginator to the model, so it can be used by the view for rendering */
Tags are provided to simplify the view – illustrated below. A strength of the tags is that the results themselves can be displayed any way you want – it doesn’t have to be a table. (For those who need it, an additional tag is provided to automatically display table rows).
<!-- summary tag outputs something like: Showing results 20 to 30 out of 35 -->
<p><results:summary paginator="${paginator}" /></p>
<table class="results">
<c:forEach items="${myResultsList}" var="item">
<tr>table row goes here...</tr>
</c:forEach>
<table>
<!-- paginator tag outputs links something like: Prev | 1 | 2 | 3 | Next -->
<p><results:paginator paginator="${paginator}" /></p>
Sorting
First, in the view, we add a tag to write clickable header (TH) cells.
<table>
<results:easySortableHeader
columnProps="uid,heading,modifiedDate"
columnLabels="ID,Title,Date" />
<c:forEach ... > etc.
Then, in the controller, we introduce the SearchParams class.
SearchParams searchParams = SearchParamsUtil.initSearchParams(
request);
Paginator paginator = new Paginator(maxResultsPerPage,
searchParams.getCurrentPage());
List results = search.find(criteria, paginator.getIndexOfFirst(),
maxResultsPerPage, searchParams.getOrder());
paginator.setNumResults(search.getTotalNumResults());
Comparison with displaytag
Displaytag is useful for simple cases but can get messy when things get complicated. To customise links, for example, requires specifying a custom decorator class containing embedded HTML. It doesn’t provide any assistance for querying the relevant data in the first place.
Yojava doesn’t provide print-friendly or Excel functionality but these can be handled separately with SiteMesh and Spring’s AbstractExelView respectively. Additional support for Excel is planned. Print-friendly functionality isn’t specific to search results.
AJAX considerations
If you are considering AJAX to page/sort results, be aware that it doesn’t play well with SEO or tracking page/ad impressions. This is generally unimportant for admin applications.
Convenience
The previous examples have illustrated that yojava pagination isn’t limited to simple tables. When a simple table is what you want, you can do this
<results:easyCells object="${myList}"
columnProps="uid,heading,modifiedDate" />
Customisation
Most of the pagination/sorting tags are implemented as tag files which means the source is in the jar – you can use it as a basis for you own scripts. For example, the results:summary tag looks something like this
Results ${paginator.indexOfFirst} to ${paginator.indexOfLast} of ${paginator.numResults}
Basic pagination links can be written like this
<c:forEach begin="1" varStatus="status"
end="${(numPages < 10) ? numPages : 10}">
<a href='<url:replaceParam name="page"
newValue="${status.count}" />' >
${status.count}
</a>
</c:forEach>
To style alternate rows
<tr class="${util:oddEven(status.count, 'odd', 'even')}">...</tr>
GET requests and SEO
It’s good practice to have search results that support GET requests. It enables linking to search results pages, bookmarking and the possibility of browser caching.
For SEO purposes, you may even want to include the query in the path instead of the querystring where possible. For example, “/paris+holidays/results.html” instead of “/results.html?query=paris+holidays”.
When designing for SEO be wary of unwittingly creating a spider trap.
Performance and scalability
There are several strategies for querying consecutive result pages. One approach is storing results in the session to minimise the number of queries to the database (at the cost of using more memory). Depending on your application, caching may also be an option.
Using the Paginator doesn’t restrict your options. It just help keeps the code lean and clean.