1) create a custom data model that returns the correct total number of rows,
but only loads the current page of data internally
2) use tomahawk's data scroller, but you will need to customize it & the
data table to fix the "bug" it has with not using EL expressions on set.


Lazy data model:
public class LazyDataModel<T>
 extends DataModel
{
 public final static int DEFAULT_PAGE_SIZE = 20;

 private int pageSize = DEFAULT_PAGE_SIZE;
 private List<T> items;
 private int startAt = 0;
 private int rowIndex = -1;
 private int rowCount = 0;

 public int getPageSize()
 {
   return this.pageSize;
 }

 public void setPageSize(int pageCount)
 {
   if (this.pageSize == pageCount) return;
   this.pageSize = pageCount;
   items = null;
   fireRequestLoad(Reason.PAGE_SIZE_CHANGED);
 }

 public List<T> getItems()
 {
   return this.items;
 }

 public void setItems(List<T> items)
 {
   this.items = items;
 }

 @Override
 public int getRowCount()
 {
   return this.rowCount;
 }

 public void setRowCount(int rowCount)
 {
   if (this.rowCount == rowCount) return;
   this.rowCount = rowCount;
   items = null;
   fireRequestLoad(Reason.ROW_COUNT_CHANGED);
 }

 public int getRowIndex()
 {
   return this.rowIndex;
 }

 public void setRowIndex(int rowIndex)
 {
   if (this.rowIndex == rowIndex) return;
   this.rowIndex = rowIndex;

   if (rowIndex >= 0)
   {
     int startAt = rowIndex - (rowIndex % pageSize);
     if (items == null || startAt != this.startAt)
     {
       this.startAt = startAt;
       fireRequestLoad(Reason.START_AT_CHANGED);
     }
   }

   Object data = isRowAvailable() ? getRowData() : null;
   DataModelEvent event = new DataModelEvent(this, this.rowIndex, data);

   for (DataModelListener listener : getDataModelListeners())
     listener.rowSelected(event);
 }

 public int getStartAt()
 {
   return this.startAt;
 }

 /**
  * @param startAt the startAt to set
  */
 public void setStartAt(int startAt)
 {
   if (this.startAt == startAt) return;
   this.startAt = startAt;
   fireRequestLoad(Reason.START_AT_CHANGED);
 }

 /**
  * @see javax.faces.model.DataModel#getRowData()
  */
 @Override
 public Object getRowData()
 {
   if (!isRowAvailable()) return null;
   return items.get(rowIndex - startAt);
 }

 /**
  * @see javax.faces.model.DataModel#getWrappedData()
  */
 @Override
 public Object getWrappedData()
 {
   return items;
 }

 /**
  * @see javax.faces.model.DataModel#setWrappedData(java.lang.Object)
  */
 @Override @SuppressWarnings("unchecked")
 public void setWrappedData(Object data)
 {
   items = (List<T>)data;
 }

 /**
  * @see javax.faces.model.DataModel#isRowAvailable()
  */
 @Override
 public boolean isRowAvailable()
 {
   return rowIndex >= startAt &&
     items != null && rowIndex < (items.size() + startAt);
 }

 @SuppressWarnings("unchecked")
 private void fireRequestLoad(Reason reason)
 {
   LazyDataModelLoadRequestedEvent<T> evt =
     new LazyDataModelLoadRequestedEvent<T>(this, reason);

   for (DataModelListener listener : getDataModelListeners())
     if (listener instanceof LazyDataModelListener)
       ((LazyDataModelListener)listener).onRequestLoad(evt);
 }
}

Lazy data model event:
public class LazyDataModelEvent<T>
 extends DataModelEvent
{
 private LazyDataModel<T> model;

 public LazyDataModelEvent(LazyDataModel<T> model, int index, Object data)
 {
   super(model, index, data);
   this.model = model;
 }

 /**
  * @see javax.faces.model.DataModelEvent#getDataModel()
  */
 @Override
 public LazyDataModel<T> getDataModel()
 {
   return model;
 }
}

Listener:
public interface LazyDataModelListener<T>
 extends DataModelListener
{
 public void onRequestLoad(LazyDataModelLoadRequestedEvent<T> evt);
}

Load request event:
public class LazyDataModelLoadRequestedEvent<T>
 extends DataModelEvent
{
 public enum Reason
 {
   START_AT_CHANGED,
   INDEX_CHANGED,
   PAGE_SIZE_CHANGED,
   ROW_COUNT_CHANGED,
 }

 private LazyDataModel<T> model;
 private Reason reason;

 public LazyDataModelLoadRequestedEvent(LazyDataModel<T> model,
   Reason reason)
 {
   super(model, 0, -1);
   this.model = model;
   this.reason = reason;
 }

 /**
  * @see javax.faces.model.DataModelEvent#getDataModel()
  */
 @Override
 public LazyDataModel<T> getDataModel()
 {
   return model;
 }

 public Reason getReason()
 {
   return this.reason;
 }
}

Extended data table to make sure that the "first" value always comes from
the EL binding instead of the local UIData value:
/**
* UI Data component with functionality to
* store values in the backing bean using EL instead
* of in the component
*
* @author arobinson
*/
public class ExtendedELUIData
 extends HtmlDataTable
{
 /**
  * @see javax.faces.component.UIData#setFirst(int)
  */
 @Override
 public void setFirst(int first)
 {
   ValueBinding vb = getValueBinding("first");
   if (vb != null)
   {
     vb.setValue(getFacesContext(), first);
     return;
   }
   else
     super.setFirst(first);
 }
}



On 12/11/06, albartell <[EMAIL PROTECTED]> wrote:

 Now that I know the datascroller component most likely will not work for
my large datasets, I am curious to know how others that are writing
enterprise level apps with MyFaces have accomplished the task.  Note that my
problem with datascroller is that you can't initialize it to be at a
particular page and have it reflect that in the hyperlinked page numbers.

*Specifically:*

1. How are you loading large datasets x number of records at a time into
MyFaces components? (I have figured this out using my own method which is
detailed in this forum post: *http://tinyurl.com/ym2l73*)

2. What UI component are you using to allow the user to go to the
next/previous page? Did you create your own? Is there an existing component?

Thanks in advance for any help,
Aaron Bartell

Reply via email to