Do you know this feeling when every code you write works immedietly and you underrun your schedule :-P It's like 'oh yeah now I have time to make it perfect'. That's where I am at the moment^^
So I implemented a repeater with JSF (ui:repeat) and I thought about a paging for all the entities. Is there maybe an easy way to do that? What are the points I have to think about?
Would be nice if someone gives me some help. My googleskills haven't helped me so far :-P
Cheers...
Here is a simple example that should give you the idea on how to implement this.
RepeatPaginator:
public class RepeatPaginator {
private static final int DEFAULT_RECORDS_NUMBER = 2;
private static final int DEFAULT_PAGE_INDEX = 1;
private int records;
private int recordsTotal;
private int pageIndex;
private int pages;
private List<?> origModel;
private List<?> model;
public RepeatPaginator(List<?> model) {
this.origModel = model;
this.records = DEFAULT_RECORDS_NUMBER;
this.pageIndex = DEFAULT_PAGE_INDEX;
this.recordsTotal = model.size();
if (records > 0) {
pages = records <= 0 ? 1 : recordsTotal / records;
if (recordsTotal % records > 0) {
pages++;
}
if (pages == 0) {
pages = 1;
}
} else {
records = 1;
pages = 1;
}
updateModel();
}
public void updateModel() {
int fromIndex = getFirst();
int toIndex = getFirst() + records;
if(toIndex > this.recordsTotal) {
toIndex = this.recordsTotal;
}
this.model = origModel.subList(fromIndex, toIndex);
}
public void next() {
if(this.pageIndex < pages) {
this.pageIndex++;
}
updateModel();
}
public void prev() {
if(this.pageIndex > 1) {
this.pageIndex--;
}
updateModel();
}
public int getRecords() {
return records;
}
public int getRecordsTotal() {
return recordsTotal;
}
public int getPageIndex() {
return pageIndex;
}
public int getPages() {
return pages;
}
public int getFirst() {
return (pageIndex * records) - records;
}
public List<?> getModel() {
return model;
}
public void setPageIndex(int pageIndex) {
this.pageIndex = pageIndex;
}
}
Bean:
public class TestBean {
private List<String> list;
private RepeatPaginator paginator;
@PostConstruct
public void init() {
this.list = new ArrayList<String>();
this.list.add("Item 1");
this.list.add("Item 2");
this.list.add("Item 3");
this.list.add("Item 4");
this.list.add("Item 5");
this.list.add("Item 6");
this.list.add("Item 7");
this.list.add("Item 8");
this.list.add("Item 9");
this.list.add("Item 10");
this.list.add("Item 11");
paginator = new RepeatPaginator(this.list);
}
public RepeatPaginator getPaginator() {
return paginator;
}
}
XHTML:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
template="/WEB-INF/template/default.xhtml">
<ui:define name="content">
<h:form>
<ui:repeat value="#{testBean.paginator.model}" var="listItem">
<div>
<h:outputText value="#{listItem}"/>
</div>
</ui:repeat>
<h:commandButton value="< prev" action="#{testBean.paginator.prev}"/>
<h:outputText value="#{testBean.paginator.pageIndex} / #{testBean.paginator.pages}"/>
<h:commandButton value="next >" action="#{testBean.paginator.next}"/>
<h:inputHidden value="#{testBean.paginator.pageIndex}"/>
</h:form>
</ui:define>
</ui:composition>
Pagination is in fact easy. You just have to keep passing one or two parameters around: firstrow
and optionally rowcount
(which can also be kept in the server side). When the enduser clicks Next, you just increment the value of firstrow
with the value of rowcount
. When the enduser clicks Back, you just decrement the value of firstrow
with the value of rowcount
. You only need to check if it doesn't exceed the borders of 0
and totalrows
and alter accordingly.
Then, based on the desired firstrow
and rowcount
, you know exactly which data to display. If all the data is already in some List
in Java's memory, then you just use List#subList()
to obtain a sublist from it for display. It is however not efficient to duplicate the entire database table into Java's memory. It may not harm when it's only 100 rows, but when it's much more than that and/or you're duplicating it for every single user, then the application will run out of memory very soon.
In that case you'd rather like to paginate at DB level. In for example MySQL you can use LIMIT
clause to obtain a subset of results from the DB. JPA/Hibernate even provides ways using setFirstResult()
and setMaxResults()
methods of Query
and Criteria
respectively. You can find examples in this and this answer.
You can find a basic JSF 1.2 targeted kickoff example of Google-like pagination (and sorting) in this article. It uses Tomahawk components, but in JSF 2.0 you can just leave them away by making the bean @ViewScoped
(replaces t:saveState
) and using ui:repeat
(replaces t:dataList
).
Last but not least, there are lot of component libraries which does all the works in a single component. For example RichFaces <rich:datascroller>
and PrimeFaces <p:dataTable paginator="true">
(can also be done ajaxical).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With