-
1. Re: Why is pagination that complex in JSF?
healeyb Aug 15, 2012 9:19 AM (in response to alp)1 of 1 people found this helpfulTo an extent a lot of the complexity is unavoidable but I think that there's a lot of room for improvement in the documentation.
Based initially on the example from Practical Richfaces 2 but with quite a lot of changes I've written a generic abstract class
to make this a bit easier and this requires only that you override 3 abstract methods:
- getDataList - return a List of the generic type of the concrete instantiation of the abstract class. If you're using JPA you'd
typically be using something like: query.setFirstResult(firstRow).setMaxResults(numRows).getResultList();
- getKey - return the row id.
- getTotalCount - the overall number of records in the view.
I normally use this by instantiating an anonymous inner class as shown in the comments in the code. I've had to modify this
code to handle using iterationStatusVar and using row level ajax updates (render="table:@rows(...):...", so it ought to be
reasonably mature.
Regards,
Brendan.
RichLazyDataModel.java
------------------------------------
package uk.co.myco.jsfbeans.helper;
import java.util.List;
import java.util.ListIterator;
import javax.faces.context.FacesContext;
import org.ajax4jsf.model.DataVisitor;
import org.ajax4jsf.model.ExtendedDataModel;
import org.ajax4jsf.model.Range;
import org.ajax4jsf.model.SequenceRange;
/*
* @author Brendan Healey
*
* Works with a RichFaces rich:extendedDataTable, like this:
*
* private RichLazyDataModel<Event> model;
*
* model = new RichLazyDataModel<Event>() {
*
* @Override
* public List<Event> getDataList(int firstRow, int numRows) {
* return selectedEntity.getEventList(firstRow, numRows);
* }
*
* @Override
* public Object getKey(Event t) {
* return t.getId();
* }
*
* @Override
* public int getTotalCount() {
* return selectedEntity.getEventListCount().intValue();
* }
* };
*
*/
public abstract class RichLazyDataModel<T> extends ExtendedDataModel<T> {
private SequenceRange cachedRange = null;
private Integer cachedRowCount = null;
private List<T> cachedList = null;
private Object rowKey;
public abstract List<T> getDataList(int firstRow, int numRows);
public abstract Object getKey(T t);
public abstract int getTotalCount();
public RichLazyDataModel() {
super(); // shouldn't be needed try removing
}
@Override
public void walk(FacesContext ctx, DataVisitor dv, Range range, Object argument) {
SequenceRange sr = (SequenceRange) range;
if (cachedList == null || !equalRanges(cachedRange, sr)) {
int firstRow = sr.getFirstRow();
int numRows = sr.getRows();
//Log.log("firstRow: " + firstRow + " numRows: " + numRows);
cachedList = getDataList(firstRow, numRows);
cachedRange = sr;
}
for (T t : cachedList) {
if (getKey(t) == null) {
/*
* the 2nd param is used to build the client id of the table
* row, i.e. mytable:234:inputname, so don't let it be null.
*/
throw new IllegalStateException("found null key");
}
dv.process(ctx, getKey(t), argument);
}
}
/*
* The rowKey is the id from getKey, presumably obtained from
* dv.process(...).
*/
@Override
public void setRowKey(Object rowKey) {
this.rowKey = rowKey;
}
@Override
public Object getRowKey() {
return rowKey;
}
@Override
public boolean isRowAvailable() {
return (getRowData() != null);
}
@Override
public int getRowCount() {
if (cachedRowCount == null) {
cachedRowCount = getTotalCount();
}
return cachedRowCount;
}
@Override
public T getRowData() {
for (T t : cachedList) {
if (getKey(t).equals(this.getRowKey())) {
return t;
}
}
return null;
}
protected static boolean equalRanges(SequenceRange range1, SequenceRange range2) {
if (range1 == null || range2 == null) {
return range1 == null && range2 == null;
} else {
return range1.getFirstRow() == range2.getFirstRow()
&& range1.getRows() == range2.getRows();
}
}
/*
* get/setRowIndex are used when doing multiple select in an
* extendedDataTable, apparently. Not tested. Actually, the get method is
* used when using iterationStatusVar="it" & #{it.index}.
*/
@Override
public int getRowIndex() {
if (cachedList != null) {
ListIterator<T> it = cachedList.listIterator();
while (it.hasNext()) {
T t = it.next();
if (getKey(t).equals(this.getRowKey())) {
return it.previousIndex() + cachedRange.getFirstRow();
}
}
}
return -1;
}
@Override
public void setRowIndex(int rowIndex) {
int upperBound = cachedRange.getFirstRow() + cachedRange.getRows();
if (rowIndex >= cachedRange.getFirstRow() && rowIndex < upperBound) {
int index = rowIndex % cachedRange.getRows();
T t = cachedList.get(index);
setRowKey(getKey(t));
}
}
@Override
public Object getWrappedData() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void setWrappedData(Object data) {
throw new UnsupportedOperationException("Not supported yet.");
}
public List<T> getCachedList() {
return cachedList;
}
}
-
2. Re: Why is pagination that complex in JSF?
puspendu.banerjee Aug 16, 2012 1:34 PM (in response to alp)Server Side Pagination was always complex, but now a day, a number of framework e.g. JBoss Seam has made life a lot easier.
Give it a try!
-
3. Re: Why is pagination that complex in JSF?
healeyb Aug 16, 2012 2:14 PM (in response to puspendu.banerjee)Puspendu, could you be more specific as to how Seam is able to help in this respect? Any personal experience you
have is likely to be of interest to the wider community, but perhaps provide more details,
Regards,
Brendan.
-
4. Re: Why is pagination that complex in JSF?
puspendu.banerjee Aug 17, 2012 12:20 AM (in response to healeyb)My Personal Experience with seam is poor though you may read this blog.
-
5. Re: Why is pagination that complex in JSF?
alp Aug 17, 2012 4:44 AM (in response to healeyb)Thanx Brendan,
i was thinking about nearly the same approach- create some abstract wrapper around ExtendedDataModel not to invent a wheel every time i need a paged list.
My final solution is to utilize your RichLazyDataModel and store list state (current page, search criteria, sort orde) in the session
I just wonder why such a simple and needed solution is not a part of the framework. All you need is a standard way of storing list state and pagable data model
Had there been something like RichLazyDataModel and clear example how to implement simple paged list with it i wouldn't have wasted several days.
Do you mind if i repost your code on stackoverflow with a refference to you?
-
6. Re: Why is pagination that complex in JSF?
alp Aug 17, 2012 4:52 AM (in response to puspendu.banerjee)Hi Puspendu,
my main concern was not not being able to implement paged list in jsf, but unnecessary complexity of existing solutions.
I heard of Seam many time and would love to try it in the future projects, but it looks excessive introducing dependency on whole big framework just to split list into pages
-
7. Re: Why is pagination that complex in JSF?
healeyb Aug 17, 2012 6:05 AM (in response to alp)>Do you mind if i repost your code on stackoverflow with a refference to you?
Please feel free.
Regards,
Brendan.
-
8. Re: Why is pagination that complex in JSF?
puspendu.banerjee Aug 17, 2012 6:23 AM (in response to alp)Alexy,
Well, Seam is not very large at it's core but your dependency graph becomes larger as you start to use more features.
Regards,
-
9. Re: Why is pagination that complex in JSF?
yzstone Jun 12, 2013 12:03 AM (in response to healeyb)Hello Brendan,
Thank you very much for posting the code for an abstract lazy loading data model. The code looks clean and straightforward. I was trying to use the class in my project but got an error. See below for the abridged error message:
java.lang.IllegalArgumentException: Page size must not be less than or equal to zero!
at org.springframework.data.domain.PageRequest.<init>(PageRequest.java:74)
at my.beans.JobFilter.filterJobs(JobFilter.java:247)
at my.beans.JobsBean$DataModel.getDataList(JobsBean.java:103)
at util.RichLazyDataModel.walk(RichLazyDataModel.java:65)
at org.richfaces.model.ArrangeableModel.initializeRowKeys(ArrangeableModel.java:225)
at org.richfaces.model.ArrangeableModel.arrange(ArrangeableModel.java:194)
at org.richfaces.component.UIDataTableBase.createExtendedDataModel(UIDataTableBase.java:265)
at org.richfaces.component.UIDataAdaptor.getExtendedDataModel(UIDataAdaptor.java:459)
at org.richfaces.component.UIDataAdaptor.getRowCount(UIDataAdaptor.java:510)
...
at org.richfaces.DataScrollerUtils.eval(DataScrollerUtils.java:86)
at org.richfaces.DataScrollerUtils.getRowCount(DataScrollerUtils.java:81)
at org.richfaces.event.DataTablePreRenderListener.processEvent(DataTablePreRenderListener.java:122)
The exception was thrown by Spring Data because the specified page size (or numRows as the second parameter of RichLazyDataModel.getDataList()) was -1, which was originated from ArrangeableModel (line 225, RichFaces 4.3.2):
originalModel.walk(context, new DataVisitor() {
public DataVisitResult process(FacesContext context, Object rowKey, Object argument) {
originalModel.setRowKey(rowKey);
if (originalModel.isRowAvailable()) {
rowKeys.add(rowKey);
}
return DataVisitResult.CONTINUE;
}
}, new SequenceRange(0, -1), null);
I am not sure why RichFaces called walk() to figure out the row count and why -1 was used instead of my specified page size (by the "rows" attribute of the data table). Here is my code extending the lazy data model, as a static inner class of my view scoped bean:
/**
* Data model for supporting server side pagination.
*/
private static class DataModel extends RichLazyDataModel<AbstractJob> implements Serializable {
private static final long serialVersionUID = 1L;
@Override
public List<AbstractJob> getDataList(int firstRow, int numRows) {
return FacesHelper.findBean(JobFilter.class).filterJobs(firstRow, numRows);
}
@Override
public Object getKey(AbstractJob t) {
return t.getId();
}
@Override
public int getTotalCount() {
return FacesHelper.findBean(JobFilter.class).countJobs();
}
}
In my JSF page, I used <rich:extendedDataTable>. I also tried dataTable with no different result.
<rich:extendedDataTable
id="table" value="#{jobsBean.dataModel}" var="r"
rows="20" rowClasses="odd,even">
...
<f:facet name="footer">
<rich:dataScroller maxPages="9" fastControls="hide"/>
</f:facet>
</rich:extendedDataTable>
Did I miss something or do something wrong? I am developing on a Win7 box with Tomcat 7, if that matters at all.
Thanks a lot for your help,
Yanlin