How to do Lazy Pagination using JSF-Richfaces-SEAM?

Let me take you to, how to do dynamic pagination using  rich:dataTable, rich:datascroller, org.ajax4jsf.model.SerializableDataModel.  We will get into a step by step approach on implementing Lazy Pagination using JSF-Richfaces-SEAM.

Lazy Pagination using JSF-Richfaces-SEAM

Please Note: For Richfaces 4 usage, after downloading DynamicDataModel.java. Please do following two bullet points change to java class file. SerializableDataModel was removed in release of Richfaces 4.

  • Replace extends class from org.ajax4jsf.model.SerializableDataModel to org.ajax4jsf.model.ExtendedDataModel
  • Remove following method from class file
/* (non-Javadoc)
 * @see org.ajax4jsf.model.SerializableDataModel#update()
 */
@Override
public void update() {
	// ignore
}


How to do Step by Step approach!

Step 1:

Create a Class DynamicDataModel extending org.ajax4jsf.model.SerializableDataModel; with all overridden methods.

public class DynamicDataModel extends SerializableDataModel {

    @Override
    public void update() {
    	// TODO Auto-generated method stub
    }

    @Override
    public Object getRowKey() {
        // TODO Auto-generated method stub
    	return null;
    }

    @Override
    public void setRowKey(Object key) {
        // TODO Auto-generated method stub
    }

    @Override
    public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {
        // TODO Auto-generated method stub
    }

    @Override
    public int getRowCount() {
    	// TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Object getRowData() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getRowIndex() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Object getWrappedData() {
        // TODO Auto-generated method stub
    	return null;
    }

    @Override
    public boolean isRowAvailable() {
    	// TODO Auto-generated method stub
        return false;
    }

    @Override
    public void setRowIndex(int rowIndex) {
        // TODO Auto-generated method stub
    }

    @Override
    public void setWrappedData(Object data) {
        // TODO Auto-generated method stub
    }
}

Step 2:

Declare below given local variables to manipulate the pagination in the class.

private Integer rowId;
private Map wrappedData = new HashMap();
private List wrappedKeys = null;
private Integer totalRows = 0;
private Integer firstRow = 0;
private Integer numberOfRows = 0;

Step 3:

And now we have to implement following methods to achieve dynamic loading of limited data required to display on the page.

@Override
public Object getRowKey() {
	return rowId;
}

@Override
public void setRowKey(Object key) {
	this.rowId = (Integer) key;
}

@Override
public int getRowCount() {
	if (null == rowCount) {
		return rowCount.intValue();
	}
	return 0;
}

@Override
public Object getRowData() {
	if (null == rowId) {
		return null;
	}
	return wrappedData.get(rowId);
}

public SerializableDataModel getSerializableModel(Range range) {
	if (null != wrappedKeys) {
		return this;
	}
	return null;
}

Step 4:

Ignore following methods since they are never invoked by framework. However you can make use of it on need basis.

@Override
public int getRowIndex() {
	return 0;
}

@Override
public Object getWrappedData() {
	throw new UnsupportedOperationException();
}

@Override
public boolean isRowAvailable() {
	if (rowId == null) {
		return false;
	} else {
		return dataProvider.hasItemByRowId(rowId);
	}
}

@Override
public void setRowIndex(int rowIndex) {
	// ignore
}

@Override
public void setWrappedData(Object data) {
	throw new UnsupportedOperationException();
}

Step 5: 

Now I’m gonna explain the method walk(), vital part of implementation.

public void walk(FacesContext context, DataVisitor visitor, Range range, Object argument) throws IOException {

	// This is method we have to populate a data for DataTable
	//Range parameter provides the firstRow value and no. of rows value. Extract those values to local variables
	this.firstRow = ((SequenceRange) range).getFirstRow();
	this.numberOfRows = ((SequenceRange) range).getRows();

	//we can populate the data from Service Call/Web Service Call For eg. Data from database or Search results using Range values
	//Define getItemsByrange() method to extract the required data by passing the range values
	List sampleDataList = getItemsByrange(this.firstRow, this.numberOfRows.intValue());

	// Now store the data to wrappedKeys & wrappedData components for the framework to make use of.
	wrappedKeys = new ArrayList();
	if (!sampleDataList.isEmpty()) {
		for (SampleData item : sampleDataList) {
			wrappedKeys.add(item.getId());
			wrappedData.put(item.getId(), item);
			visitor.process(context, item.getId(), argument);
		}
	}
}

Step 6: 

We have completed Java part; let’s move on to JSF code snippet to invoke the Java class

<rich:panel>
	<rich:datascroller for="sampleData" maxpages="10" id="dsTop" fastcontrols="hide" renderifsinglepage="false" rerender="dsBtm"></rich:datascroller>
	<rich:datatable id="sampleData" rowclasses="greyBg, whiteBg" styleclass="tablesorter" value="#{dynamicDataModel}" var="item" rowkeyvar="row" rows="10" width="674" border="0" cellspacing="0" cellpadding="0">
		<rich:column sortable="true" sortby="#{item.id}">
			<f:facet styleclass="header" name="header">
				<h:outputtext value="ID"> </h:outputtext>
			</f:facet>
			<h:outputtext value="#{item.id}"></h:outputtext>
		</rich:column>
		<rich:column sortable="true" sortby="#{item.firstName}">
			<f:facet styleclass="header" name="header">
				<h:outputtext value="First Name"></h:outputtext>
			</f:facet>
			<h:outputtext value="#{item.firstName}"></h:outputtext>
		</rich:column>
		<rich:column sortable="true" sortby="#{item.lastName}">
			<f:facet styleclass="header" name="header">
				<h:outputtext value="Last Name"></h:outputtext>
			</f:facet>
			<h:outputtext value="#{item.lastName}"></h:outputtext>
		</rich:column>
	</rich:datatable>
	<rich:datascroller for="sampleData" maxpages="10" id="dsBtm" fastcontrols="hide" renderifsinglepage="false" rerender="dsTop"></rich:datascroller>
</rich:panel>

That’s all we have to do above steps to achieve dynamic data model pagination through Lazy Pagination using JSF-Richfaces-SEAM. This approach could minimize the weight on presentation layer loading the required data for the page rather loading all the data; that are yet to be displayed in forthcoming pages assuming the data extraction calls are not so expensive.


Downloads

Code files used in the article.  Now all the Code Snippets and related files will be shared from GitHub Repo.

If you have any queries please leave a comment.