Few days back I wanted to take richfaces on a test drive. The organization I work in has lots of requirements for reports. I wanted to introduce JSF for its rapid and component based development. I found richfaces extremely nice to start with.
First thing on my mind was "can richfaces handle server side paging? i.e. only load chunk of data from the database that is required" since there could be a report which could bring 1,000,000 records. So if I want to show 10 records per page, is it possible to load only 10 records from the database instead of 1,000,000.
Richfaces does provide this functionality which I found on this page.
http://gochev.blogspot.com/2009/08/richfaces-server-side-paging-with.html
It worked. It uses datatable and datascroller with SerializableDataModel and ExtendedDataModel. This solution is good but I wanted something else. I wanted an easier solution. I wanted something like a pageNumber in my ManagedBean whenever a page number in datascroller is clicked and load chunk of data using that pageNumber and numberOfRecordsPerPage(lets say 10).
First I thought of making my own datascroller component by extending the richfaces datascroller component. Since I dont have any experience of JSF custom component development right now, so I changed my mind to modification of existing datascroller component instead.
After hitting here and there I finally got it working. Its not complete in all aspects but it atleast works. If someone makes it better, please share.
<rich:datascroller has an UI Class HtmlDatascroller.java which extends UIDatascroller.java
In UIDatascroller.java, create a property named as pageIndexValue and its getter method.
private int pageIndexValue;
public int getPageIndexValue() {
return pageIndexValue;
}
There is a function in UIDatscroller.java that returns a page number. Set pageIndexValue before return. (Changes are in italic-bold)
public int getPageForFacet(String facetName) {
if (facetName == null) {
throw new NullPointerException();
}
int newPage = 1;
int pageCount = getPageCount();
if (FIRST_FACET_NAME.equals(facetName)) {
newPage = 1;
} else if (PREVIOUS_FACET_NAME.equals(facetName)) {
newPage = getPage() - 1;
} else if (NEXT_FACET_NAME.equals(facetName)) {
newPage = getPage() + 1;
} else if (LAST_FACET_NAME.equals(facetName)) {
newPage = pageCount > 0 ? pageCount : 1;
} else if (FAST_FORWARD_FACET_NAME.equals(facetName)) {
newPage = getPage() + getFastStepOrDefault();
} else if (FAST_REWIND_FACET_NAME.equals(facetName)) {
newPage = getPage() - getFastStepOrDefault();
} else {
try {
newPage = Integer.parseInt(facetName.toString());
} catch (NumberFormatException e) {
throw new FacesException(e.getLocalizedMessage(), e);
}
}
if (newPage >= 1 && newPage <= pageCount) {
pageIndexValue = newPage;
return newPage;
} else {
pageIndexValue = 0;
return 0;
}
}
Now you have set the pageIndexValue that can be accessed via getter method from ManagedBean.
We create another property and now its setter method in UIDatscroller.java
private int pageCount;
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
this will be used from ManagedBean to set the total number of pages.
Note: we will load only chuck of data in ManagedBean but scroller has to show total number of pages.
pageCount = TotalRecordCount / PageSize
Now modify two more methods in UIDatscroller.java
public int getPageCount(UIData data, int rowCount, int rows) {
//Comment all these lines as below
/*int pageCount;
if (rows > 0) {
pageCount = rows <= 0 ? 1 : rowCount / rows;
if (rowCount % rows > 0) {
pageCount++;
}
if (pageCount == 0) {
pageCount = 1;
}
} else {
rows = 1;
pageCount = 1;
}*/
//Comment till this point
return pageCount;
}
Same goes with this method
/** @return the page count of the uidata */
public int getPageCount() {
//return getPageCount(getDataTable());//Comment this line
return pageCount; //Add this line
}
Comment a line in this method and add your own in the end.
public void setupFirstRowValue() {
UIData dataTable = getDataTable();
if (isRendered()) {
int rowCount = getRowCount(dataTable);
int rows = getRows(dataTable);
Integer pageCount = getPageCount(dataTable, rowCount, rows);
int page = getPage();
int newPage = -1;
if (page < 1) {
newPage = 1;
} else if (page > pageCount) {
newPage = (pageCount != 0 ? pageCount : 1);
}
if (newPage != -1) {
FacesContext context = getFacesContext();
Object label = MessageUtil.getLabel(context, this);
String formattedMessage = Messages.getMessage(Messages.DATASCROLLER_PAGE_MISSING,
new Object[] {label, page, pageCount, newPage});
log.warn(formattedMessage);
page = newPage;
dataTable.getAttributes().put(SCROLLER_STATE_ATTRIBUTE, page);
}
if (isRendered(dataTable)) {
int first;
String lastPageMode = getLastPageMode();
if (lastPageMode == null) {
lastPageMode = LAST_PAGE_MODE_SHORT;
} else if (!LAST_PAGE_MODE_SHORT.equals(lastPageMode) &&
!LAST_PAGE_MODE_FULL.equals(lastPageMode)) {
throw new IllegalArgumentException("Illegal value of 'lastPageMode' attribute: '" + lastPageMode + "'");
}
if (page != pageCount || LAST_PAGE_MODE_SHORT.equals(lastPageMode)) {
first = (page - 1) * rows;
} else {
first = rowCount - rows;
if (first < 0) {
first = 0;
}
}
//dataTable.setFirst(first);//Comment this line
dataTable.setFirst(0);//Add this line
}
}
}
In myBean (ManagedBean), do this
HtmlDatascroller myScroller = new HtmlDatascroller();
public HtmlDatascroller getMyScroller() {
myScroller = (HtmlDatascroller) findComponentInRoot("myScroller");
return myScroller;
}
public void setMyScroller(HtmlDatascroller myScroller) {
this.myScroller = myScroller;
myScroller.setPageCount(20);
try {
this.pageNumber = myScroller.getPageIndexValue();
} catch(Exception e) {
}
}
public List getList() {
if(myScroller!= null)
pageNumber = myScroller.getPageIndexValue();
//do your data loading stuff here
// I did this to check my logic. So this data gets loaded everytime with page number
List list = new ArrayList();
list.add(new InfoBO("Name 1", String.valueOf(pageNumber), "City 1"));
list.add(new InfoBO("Name 2", String.valueOf(pageNumber), "City 2"));
list.add(new InfoBO("Name 3", String.valueOf(pageNumber), "City 3"));
list.add(new InfoBO("Name 4", String.valueOf(pageNumber), "City 4"));
list.add(new InfoBO("Name 5", String.valueOf(pageNumber), "City 5"));
list.add(new InfoBO("Name 6", String.valueOf(pageNumber), "City 6"));
list.add(new InfoBO("Name 7", String.valueOf(pageNumber), "City 7"));
list.add(new InfoBO("Name 8", String.valueOf(pageNumber), "City 8"));
list.add(new InfoBO("Name 9", String.valueOf(pageNumber), "City 9"));
list.add(new InfoBO("Name 10", String.valueOf(pageNumber), "City 10"));
return list;
}
In Page, do this
<rich:dataTable align="center" id="userInfo" rows="10"
onRowMouseOver="this.style.backgroundColor='#F1F1F1'"
onRowMouseOut="this.style.backgroundColor='#{a4jSkin.tableBackgroundColor}'"
cellpadding="0" cellspacing="0"
width="700" border="0" var="myRec" value="#{myBean.list}">
<f:facet name="header">
<rich:columnGroup>
<rich:column colspan="3">
<h:outputText value="User Info" />
</rich:column>
<rich:column breakBefore="true">
<h:outputText value="Name" />
</rich:column>
<rich:column>
<h:outputText value="pageNumber" />
</rich:column>
<rich:column>
<h:outputText value="City" />
</rich:column>
</rich:columnGroup>
</f:facet>
<rich:column >
<h:outputText value="#{myRec.name}" />
</rich:column>
<rich:column >
<h:outputText value="#{myRec.pageNumber}" />
</rich:column>
<rich:column >
<h:outputText value="#{myRec.city}" />
</rich:column>
<f:facet name="footer">
<rich:datascroller
binding="#{myBean.myScroller}" />
</f:facet>
</rich:dataTable>
<rich:dataTable simply uses myBean.list to populate its data.
<rich:datascroller is binded with myBean.myScroller
Now a little recap,
what I've done is that I've create two properties pageIndexValue and pageCount in UIDatascroller.java.
pageIndexValue with a getter method (pageIndexValue will be provided by datascroller to ManagedBean) and pageCount with a setter method (pageCount will be set from ManagedBean to datascroller).
When UIDatascroller.java figures out the new Page Number, it sets this value to pageIndexValue which we can then get in the ManagedBean using getPageIndexValue() method.
Now, when JSF binds the datascroller to myScroller variable, we set its pageCount variable using setPageCount() method and whenever pageCount is requested during the cycle, we simply return our defined pageCount.
And in the end, we do dataTable.setFirst(0); because we will only load 10 records per page. So every recordset will have to start from 0 (zero).
Happy Programming!
And if anybody finds a complete source package of richfaces, please send me a zip file containing source for all of the following.
richfaces-api-3.3.1.GA
richfaces-impl-3.3.1.GA
richfaces-ui-3.3.1.GA
Was able to find few packages, but couldn't find them all (not even on their site).
No comments:
Post a Comment