There are two supported pagination tags in Loom used to generate tables and unordered lists:
<!-- Paged table that can be sorted by any column -->
<l:pagedTable data="${action.mortgages}" id="mortgages">
<l:column sortable="false">
<l:inputCheckbox name="selectedRows" class="selectRow checkbox" value="${row.id}" renderLabel="false"/>
</l:column>
<l:column property="id" />
<l:column property="name" action="Mortgages" event="edit">
<l:param name="mortgage.id" value="${row.id}"/>
</l:column>
<l:column property="address" />
<l:column property="principalLoanBalance" title="Loan" />
<l:column property="creationDate" class="date" />
</l:pagedTable>
<!-- Paged list that shows the pagination links both at the top and the bottom -->
<l:pagedList data="${action.customers}" linksPosition="both">
<l:url action="Customers" event="edit">
<l:param name="customer.id" value="${row.id}"/>
<strong>${row.id}</strong> - ${row.firstName} ${row.lastName} (ID: ${row.idCard})
</l:url>
</l:pagedList>
The best performance will be obtained by delegating pagination to the database. In this case, only the JPA query is required:
public class MortgagesAction extends AbstractAction {
/** controller that handles the CRUD stuff */
@Autowired
private ExtendedEntityManager transactionalService;
/** the paged list of Mortgages */
private PagedListData<Mortgage> mortgages;
/**
* Show a paged list of Mortgage instances
*/
@GET @Path("/")
@Event(defaultEvent=true)
public Resolution list() {
PagedListCriteria criteria = PagedListCriteriaFactory.create(getRequest());
criteria.setQuery("from Mortgage m order by m.id");
mortgages = transactionalService.query(criteria);
return forward();
}
}
That's it, You don't have to care about pagination and sorting. If your application must use a non-JPA backend, it is quite easy to implement your own query() method.
For applications where there is no database, the pagination can be performed at the web layer.
public class CustomersAction extends AbstractAction {
/** service class */
@Autowired
private ExtendedEntityManager transactionalService;
/** the paged list of customers */
private PagedListData<Customer> customers;
/**
* Example of manual conversion from List to PagedListData
*/
@Event(defaultEvent=true)
public Resolution list() {
PagedListCriteria criteria = PagedListCriteriaFactory.create(getRequest());
List<Customer> lc = transactionalService.findAll(Customer.class);
customers = PagedListDataFactory.create(criteria, lc);
return forward();
}
}
Pagination and sorting will again be transparent as long as all sortable columns implement Sortable.
Be aware that this practice has some drawbacks:
To translate your pagination components you should include the following entries in your messages.properties file:
paged.empty=No items found
paged.oneItem=One item found
paged.allItems=${data.totalRecords} items found, displaying all
paged.partial=${data.totalRecords} items found, displaying ${data.recordIndex + 1} to ${data.nextPageRecordIndex}
paged.previous=« Previous
paged.next=Next »
If a table must use a specific translation it can specify a "messagePrefix" attribute, which would replace the "paged" default. For example:
paged.empty=No items found
paged.oneItem=One item found
paged.allItems=${data.totalRecords} items found, displaying all
paged.partial=${data.totalRecords} items found, displaying ${data.recordIndex + 1} to ${data.nextPageRecordIndex}
previous=« Previous
next=Next »
conversations.empty=No conversations found
conversations.oneItem=One conversation found
conversations.allItems=${data.totalRecords} conversations found, displaying all
conversations.partial=${data.totalRecords} conversations found, displaying ${data.recordIndex + 1} to ${data.nextPageRecordIndex}
These entries use the same translation rules of all Loom components, so you can specify "previous" instead of "paged.previous" and "conversations.previous".
We found that paged tags are a convenient way of displaying non-paged data, so we now also accept plain Collections using the "collection" attribute.
public class CustomersAction extends AbstractAction {
@Event
public Resolution list() {
List<Customer> lc = transactionalService.findAll(Customer.class);
return forward().setAttribute("customers", lc);
}
}
<l:pagedList collection="${customers}">
...
</l:pagedList>
If you plan to display more than one table in the same HTML page you should say so, otherwise the "next" and "previous" buttons would affect both tables at once. You do that by indicating an ID when creating your PagedListRequest instance:
PagedListCriteria criteria1 = PagedListCriteriaFactory.create(getRequest(), "table1"); PagedListCriteria criteria2 = PagedListCriteriaFactory.create(getRequest(), "table2");
Paged list results are now compatible with JSON, to be used with Ajax interfaces. You may return json(pagedListData) and handle this response from the loom.ui.PagedList javascript class (see pagination.js). This will render on the browser the same javascript of the JSP tags including the browsing buttons.