11 Replies Latest reply on Jan 28, 2011 12:17 AM by sakthivel_k2003

    rich:scrollableDataTable and load on demand

    jsr34116

      Has anyone gotten rich:scrollableDataTable to only load items when needed? The user documentation states this is possible but provides no direction on how to implement it.

      I have a requirement to show the scroll bar instead of the rich:dataScroller and my table could have 100s of rows which means I cannot load them all due to performance reasons. I could find no examples or documentation that actually demonstrate this. All examples I was able to find assume a full list is returned.

      I tried returning a DataModel and a ExtendedDataModel as the value, but the rich:scrollableDataTable always fetches all of the items via get/setRowIndex.-getRowData When I return an ExtendedDataModel, the walk() method is never called. However, if I use a rich:dataTable instead of rich:scrollableDataTable, then walk() is called.

        • 1. Re: rich:scrollableDataTable and load on demand
          ilya_shaikovsky

          sorry.. your explanation isn't clear enough.. As for me I think scrollableDataTable is the component for your case - it loads data dynamixally according to scroll position.

          • 2. Re: rich:scrollableDataTable and load on demand
            jsr34116

            If I use a rich:dataDable and rich:datascroller, I can get on demand page loading of the table. I.e. whenever I click next page, or specify a specific page, that page's elements are requested. This is the behavior I want, but the only problem with this approach are its looks: its not very Web 2.0.

            If I use just a rich:scrollableDataTable (which is more Web 2.0), the entire table is loaded. It does not dynamically load depending upon scroll position. It always loads the entire table contents.

            So the question is how does one get rich:scrollableDataTable to work as advertised? Because the provided documentation does not provide any information on how to get it to page. I've tried both the 3.21 and 3.22 GA versions with no luck.

            • 3. Re: rich:scrollableDataTable and load on demand
              ilya_shaikovsky

              show us the code for scrollabledatatable please.

              • 4. Re: rich:scrollableDataTable and load on demand
                jsr34116

                here is the .xhtml:

                 <a4j:form id="viewReqTableForm">
                 <rich:scrollableDataTable id="reqtbl" var="req" value="#{viewRequestBean.dataModel}" width="100%"
                 sortMode="single" height="#{viewRequestBean.config.maxResultsPerPage * 25}px"
                 columnClasses="right,left,left,left,left,right,right,left">
                 <rich:column width="30px">
                 <h:graphicImage url="images/rightArrow.png" styleClass="imgLink" title="#{msgBndl.help}"/>
                 </rich:column>
                
                 <rich:column>
                 <f:facet name="header">
                 <h:outputText value="ID"/>
                 </f:facet>
                 <h:outputText value="#{req.requestId}"/>
                 </rich:column>
                
                 <rich:column>
                 <f:facet name="header">
                 <h:outputText value="Submitted By"/>
                 </f:facet>
                 <h:outputText value="#{req.requestor['Person-Lastname']}, #{req.requestor['Person-Firstname']}"/>
                 </rich:column>
                
                 <rich:column>
                 <f:facet name="header">
                 <h:outputText value="Beneficiary"/>
                 </f:facet>
                 <h:outputText value="#{req.beneficiary['Person-Lastname']}, #{req.beneficiary['Person-Firstname']}"/>
                 </rich:column>
                
                 <rich:column>
                 <f:facet name="header">
                 <h:outputText value="Date Submitted"/>
                 </f:facet>
                 <h:outputText value="#{req.submitDate}"/>
                 </rich:column>
                
                 <f:facet name="footer">
                 <h:outputText value="#{viewRequestBean.dataModel.numRecords} Total"/>
                 </f:facet>
                 </rich:scrollableDataTable>
                 </a4j:form>
                


                Here is the DataModel bean that viewRequestBean.getDataModel returns:

                
                public class RequestList extends PagedDataModel<USSPUserRequest, Integer>
                {
                 /** */
                 private static final long serialVersionUID = 2672142810059859813L;
                
                 /** */
                 private int m_rowCount = -1;
                 private String m_requestorPBWUserId;
                 private String m_benePBWUserId;
                 private boolean m_showAll;
                 private DBSearch m_dbs;
                 private String[] m_sortCols = { "REQUEST_ID", "REQUEST_ID", "REQ_LASTNAME", "PERSON_LASTNAME", "CHANGE_TYPE", "CREATE_DATE", "STATUS_ID"} ;
                
                 private int m_startRow;
                 private int m_endRow;
                 private List<USSPUserRequest> m_requests;
                
                 /** */
                 Logger log;
                
                 public RequestList()
                 {
                 m_requests = null;
                 m_startRow = -1;
                 m_endRow = -1;
                 m_showAll = true;
                 m_dbs = new DBSearch( DBUtil.PRODUCT_PREFIX + "USSP_RUNTIME_V");
                 }
                
                 public void setRequestor( String pbwuserid)
                 {
                 m_requestorPBWUserId = pbwuserid;
                 }
                
                 public void setBeneficiary( String pbwuserid)
                 {
                 m_benePBWUserId = pbwuserid;
                 }
                
                 /**
                 * @see PaginatingDataModel#getId(java.lang.Object)
                 */
                 @Override
                 public Integer getId( USSPUserRequest object)
                 {
                 return new Integer( object.getId());
                 }
                
                 /**
                 * @see PaginatingDataModel#findObjects(int, int, java.lang.String, boolean)
                 */
                 @Override
                 public List<USSPUserRequest> findObjects( int firstRow, int numberOfRows, String sortField, boolean descending)
                 {
                 return getRequests( firstRow, numberOfRows, sortField, descending);
                 }
                
                 /**
                 * @see PaginatingDataModel#getObjectById(java.lang.Object)
                 */
                 @Override
                 public USSPUserRequest getObjectById( Integer id)
                 {
                 return getRequest( id.intValue());
                 }
                
                 /**
                 * @see PaginatingDataModel#getDefaultSortField()
                 */
                 @Override
                 public String getDefaultSortField()
                 {
                 return "5"; // default to date submitted
                 }
                
                 /**
                 * @see PaginatingDataModel#getNumRecords()
                 */
                 @Override
                 public int getNumRecords()
                 {
                 if (m_rowCount != -1)
                 return m_rowCount;
                
                 Connection conn = null;
                 Statement stmt = null;
                 ResultSet rs = null;
                 Logger log = Logger.getLogger( this.getClass());
                 StringBuffer sql = new StringBuffer();
                 m_rowCount = 0;
                
                 sql.append( "SELECT COUNT(*) FROM");
                 sql.append( " (SELECT DISTINCT REQ_BENE_REL_ID,REQUESTOR_PBWUSERID,PBWUSERID FROM ");
                 sql.append( DBUtil.PRODUCT_PREFIX + "USSP_RUNTIME_V");
                 sql.append( " WHERE (REQUESTOR_PBWUSERID='");
                 sql.append( m_requestorPBWUserId);
                 sql.append( "' OR PBWUSERID='");
                 sql.append( m_benePBWUserId);
                 sql.append( "')");
                 sql.append( ")");
                
                 try
                 {
                 conn = DAOFactory.getDatabaseDAO().getConnection();
                 stmt = conn.createStatement();
                 log.debug( "[RequestList]:" + sql.toString());
                 rs = stmt.executeQuery( sql.toString());
                
                 if (rs.next())
                 m_rowCount = rs.getInt( 1);
                
                 log.debug( "[RequestList]: count=" + m_rowCount);
                 }
                 catch (SQLException e)
                 {
                 log.debug( "[RequestList]:" + e.getLocalizedMessage(), e);
                 }
                 finally
                 {
                 DBUtils.close( conn, stmt, rs);
                 }
                
                 return m_rowCount;
                 }
                
                 public USSPUserRequest getRequest( int key)
                 {
                 if (m_requests == null)
                 {
                 System.out.println( "@@@ no search done yet!");
                 getRequests( 1, 20, null, true);
                 }
                
                 USSPUserRequest ret = new USSPUserRequest();
                 ret.setId( key);
                
                 int idx = m_requests.indexOf( ret);
                System.out.println( "@@@ get item " + key + " idx="+idx);
                 if (idx == -1)
                 return null;
                 else
                 return m_requests.get( idx);
                 }
                
                 public List<USSPUserRequest> getRequests( int startRow, int endRow, String sortCol, boolean decending)
                 {
                System.out.println( "@@@ getRequests sort col="+sortCol+" desc="+decending);
                 if (m_requests != null && m_startRow == startRow && m_endRow == endRow)
                 return m_requests;
                
                 if (startRow == 0)
                 startRow = 1;
                
                System.out.println( "### querying db from starting row: " + startRow);
                 Connection conn = null;
                 PreparedStatement ps = null;
                 Logger log = Logger.getLogger( this.getClass());
                
                 m_requests = new ArrayList<USSPUserRequest>();
                 int orderBy = 0;
                
                 try
                 {
                 orderBy = Integer.parseInt( sortCol);
                 }
                 catch (NumberFormatException e)
                 {
                 log.debug( "invalid sort column '" + sortCol + "' specified.");
                 }
                 if (orderBy < 0 || orderBy > m_sortCols.length)
                 orderBy = 0;
                
                 m_dbs.setOrderBy( m_sortCols[orderBy]);
                 m_dbs.setSortDecending( decending);
                
                 m_dbs.setStartIndex( startRow);
                 m_dbs.setPageSize( endRow);
                 m_startRow = startRow;
                 m_endRow = endRow;
                
                 try
                 {
                 conn = DAOFactory.getDatabaseDAO().getConnection();
                 ps = conn.prepareStatement( m_dbs.getQuery(),
                 ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
                
                 USSPUserRequest builder = new USSPUserRequest();
                 Collection c = m_dbs.performSearch( ps, builder);
                 m_requests.addAll( c);
                 log.debug( "[RequestList]: found " + m_requests.size() + " requests");
                System.out.println( "@@@ found " + m_requests.size() + " requests");
                 }
                 catch (SQLException e)
                 {
                 log.debug( "[RequestList]:" + m_dbs.getQuery());
                 log.debug( "[RequestList]:" + e.getLocalizedMessage(), e);
                 }
                 finally
                 {
                 DBUtils.close( conn, ps, null);
                 }
                
                 return m_requests;
                 }
                
                 public void refresh()
                 {
                 currentPk = null;
                 detached = false;
                 wrappedKeys.clear();
                 wrappedData.clear();
                 rowCount = null;
                 m_requests = null;
                 setPageNum( 1);
                 }
                }
                
                public abstract class PagedDataModel<T, U> extends SerializableDataModel
                {
                 /** */
                 private static final long serialVersionUID = 398527390783L;
                 /** */
                 protected U currentPk;
                 /** */
                 protected boolean descending = false;
                 /** */
                 protected String sortField = getDefaultSortField();
                 /** */
                 protected boolean detached = false;
                 /** */
                 protected List<U> wrappedKeys = new ArrayList<U>();
                 /** */
                 protected Integer rowCount;
                 /** */
                 protected Map<U, T> wrappedData = new HashMap<U, T>();
                
                 private int m_rowIndex;
                
                 private int m_pageNum;
                
                 /**
                 * @see org.ajax4jsf.model.ExtendedDataModel#getRowKey()
                 */
                 public Object getRowKey()
                 {
                System.out.println( "@@@ in getRowKey="+currentPk);
                 return currentPk;
                 }
                
                 /**
                 * @see org.ajax4jsf.model.ExtendedDataModel#setRowKey(java.lang.Object)
                 */
                 public void setRowKey( final Object key)
                 {
                System.out.println( "@@@ in setRowKey="+key);
                 this.currentPk = (U) key;
                 }
                
                 /**
                 * @see org.ajax4jsf.model.SerializableDataModel#update()
                 */
                 public void update()
                 {
                System.out.println( "@@@ in update gsfo="+getSortFieldObject());
                 if (getSortFieldObject() != null)
                 {
                 final String newSortField = getSortFieldObject().toString();
                 if (newSortField.equals( sortField))
                 descending = !descending;
                
                 sortField = newSortField;
                 }
                 detached = false;
                 }
                
                 /**
                 * @return Object
                 */
                 protected Object getSortFieldObject()
                 {
                 final FacesContext context = FacesContext.getCurrentInstance();
                 final Object sortFieldObject = context.getExternalContext().getRequestParameterMap()
                 .get( "sortField");
                 return sortFieldObject;
                 }
                
                 public boolean isDescending()
                 {
                 return descending;
                 }
                
                 /**
                 * @param sortField
                 */
                 public void setSortField( final String sortField)
                 {
                System.out.println( "@@@ setSortField " + sortField);
                 if (this.sortField.equals( sortField))
                 descending = !descending;
                 else
                 this.sortField = sortField;
                 }
                
                 /**
                 * @return String
                 */
                 public String getSortField()
                 {
                System.out.println( "@@@ getSortField " + sortField);
                 return sortField;
                 }
                
                 /**
                 * @see org.ajax4jsf.model.ExtendedDataModel#getSerializableModel(org.ajax4jsf.model.Range)
                 */
                 public SerializableDataModel getSerializableModel( final Range range)
                 {
                System.out.println( "@@@ getSerializableModel");
                 if (wrappedKeys != null)
                 {
                 detached = true;
                 return null; //this;
                 }
                 return null;
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#setRowIndex(int)
                 */
                 public void setRowIndex( final int rowIndex)
                 {
                System.out.println( "@@@ setRowIndex "+rowIndex);
                 m_rowIndex = rowIndex;
                // throw new UnsupportedOperationException();
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#setWrappedData(java.lang.Object)
                 */
                 public void setWrappedData( final Object data)
                 {
                System.out.println( "@@@ setWrappedData"+data);
                 throw new UnsupportedOperationException();
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#getRowIndex()
                 */
                 public int getRowIndex()
                 {
                System.out.println( "@@@ getRowIndex "+m_rowIndex);
                 return m_rowIndex;
                // throw new UnsupportedOperationException();
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#getWrappedData()
                 */
                 public Object getWrappedData()
                 {
                System.out.println( "@@@ getWrappedData");
                 throw new UnsupportedOperationException();
                 }
                
                 /**
                 * @see org.ajax4jsf.model.ExtendedDataModel#walk(javax.faces.context.FacesContext,
                 * org.ajax4jsf.model.DataVisitor, org.ajax4jsf.model.Range,
                 * java.lang.Object)
                 */
                 public void walk(final FacesContext context, final DataVisitor visitor, final Range range, final Object argument)
                 throws IOException
                 {
                System.out.println( "@@@ walk");
                 final int firstRow = ((SequenceRange) range).getFirstRow();
                 int numberOfRows = ((SequenceRange) range).getRows();
                System.out.println( "@@@ firstRow="+firstRow);
                System.out.println( "@@@ numberOfRows="+numberOfRows);
                System.out.println( "@@@ detached="+detached);
                if (numberOfRows == 0)
                 numberOfRows = 20;
                 if (detached && getSortFieldObject() != null)
                 {
                 for (final U key : wrappedKeys)
                 {
                 setRowKey(key);
                 visitor.process(context, key, argument);
                 }
                 }
                 else
                 { // if not serialized, than we request data from data provider
                 wrappedKeys = new ArrayList<U>();
                 for (final T object : findObjects(firstRow, numberOfRows, sortField, descending))
                 {
                 wrappedKeys.add(getId(object));
                 wrappedData.put(getId(object), object);
                 visitor.process(context, getId(object), argument);
                 }
                 }
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#isRowAvailable()
                 */
                 public boolean isRowAvailable()
                 {
                System.out.println( "@@@ in isRowAvailable="+currentPk);
                 if (currentPk == null)
                 return false;
                
                 if (wrappedKeys.contains( currentPk))
                 return true;
                
                 if (wrappedData.entrySet().contains( currentPk))
                 return true;
                
                 try
                 {
                 if (getObjectById( currentPk) != null)
                 return true;
                 }
                 catch (final Exception e)
                 {
                
                 }
                 return false;
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#getRowData()
                 */
                 public Object getRowData()
                 {
                System.out.println( "@@@ in getRowData="+currentPk);
                 if (currentPk == null)
                 return null;
                
                 T object = wrappedData.get( currentPk);
                 if (object == null)
                 {
                 object = getObjectById( currentPk);
                 wrappedData.put( currentPk, object);
                 }
                 return object;
                 }
                
                 /**
                 * @see javax.faces.model.DataModel#getRowCount()
                 */
                 public int getRowCount()
                 {
                System.out.println( "@@@ getRowCount");
                 if (rowCount == null)
                 rowCount = getNumRecords();
                
                 return rowCount;
                 }
                
                 public int getPageNum()
                 {
                 return m_pageNum;
                 }
                
                 public void setPageNum( int v)
                 {
                 m_pageNum = v;
                 }
                
                 /**
                 * @param object
                 * @return U
                 */
                 public abstract U getId( T object);
                
                 /**
                 * @param firstRow
                 * @param numberOfRows
                 * @param sortField
                 * @param descending
                 * @return List<T>
                 */
                 public abstract List<T> findObjects( int firstRow, int numberOfRows, String sortField, boolean descending);
                
                 /**
                 * @param id
                 * @return T
                 */
                 public abstract T getObjectById( U id);
                
                 /**
                 * @return String
                 */
                 public abstract String getDefaultSortField();
                
                 /**
                 * @return int
                 */
                 public abstract int getNumRecords();
                }
                
                


                What I am seeing is that getRowCount() is called twice, and then a loop is entered (for all rows) where setRowIndex() is called followed by getRowData(). This returns an empty table as getRowData() is expecting the row key to be set. I can change getRowData() to work off the current row, but the real problem is that the scrollableDataTable is not paging but instead requesting all rows. Note, walk() is never called.

                If I use rich:dataTable and rich:datascroller, then these classes work fine.



                • 5. Re: rich:scrollableDataTable and load on demand
                  ilya_shaikovsky

                  you should add rows="someNumberOfRows" ;) to your sdt tag code and this will cause the data to be loaded as you need.

                  • 6. Re: rich:scrollableDataTable and load on demand
                    jsr34116

                    I did try it before, but it wasn't doing what I expected. I.e. I expected it to automatically calculate the height of the table based on the number of rows that are being displayed. Instead controls the number of rows that are shown. It doesn't make sense to me to specify both height and rows as you can specify fewer rows than will fit in the height which leaves a bunch of empty space or more rows than will fit in height. They should be mutually exclusive in my opinion and only control the height of the table.

                    Okay, retesting still yeilds no results as setRowKey() is never called as walk() is never called. The loop, however, now stops at 20 which is the number of rows I specified.

                    The real problem is why isn't walk() being called.

                    • 7. Re: rich:scrollableDataTable and load on demand
                      konstantin.mishin

                      You may try to extend ScrollableTableDataModel instead of SerializableDataModel.

                      • 8. Re: rich:scrollableDataTable and load on demand
                        harsh820

                        Hi John/konstantin,

                         

                        Were you able to make it work by extending ScrollableTableDataModel? I am trying to do the same. But I am not sure whether I am extending it correctly. This is the first time I am trying to use something which isn't given out-of-the-box by Richfaces, so in case it worked for you, please guide me.

                         

                        Here's my DataModel class.

                         

                        package com.pearson.qglobal.core.util;

                         

                         

                        import java.util.List;

                         

                         

                        import javax.persistence.EntityManager;

                         

                         

                        import org.jboss.seam.annotations.Name;

                        import org.richfaces.model.ScrollableTableDataModel;

                        import org.richfaces.model.SortOrder;

                         

                         

                        @Name(value = "qglobalDataModel")

                        public class QGlobalDataModel<T> extends ScrollableTableDataModel<T> {

                         

                         

                            private final EntityManager entityManager;

                         

                         

                            private final String ejbqlString;

                         

                         

                            private final Long totalRecords;

                         

                         

                            private List<T> displayedList;

                         

                         

                            @SuppressWarnings("unchecked")

                            public QGlobalDataModel(final EntityManager entityManager, final String ejbqlString) {

                                super();

                                this.entityManager = entityManager;

                                this.ejbqlString = ejbqlString;

                         

                         

                                this.totalRecords = (Long) this.entityManager.createQuery("select count(e) " + this.ejbqlString)

                                .getSingleResult();

                         

                         

                                this.displayedList = this.entityManager.createQuery(this.ejbqlString).setFirstResult(0).setMaxResults(10)

                                .getResultList();

                         

                         

                            }

                         

                         

                            @SuppressWarnings(QGlobalConstants.UNCHECKED)

                            @Override

                            public List<T> loadData(final int startRow, final int endRow, final SortOrder sortOrder) {

                         

                         

                                this.displayedList =

                                    this.entityManager.createQuery(this.ejbqlString).setFirstResult(startRow)

                                    .setMaxResults(endRow - startRow).getResultList();

                                this.setWrappedData(this.displayedList);

                                return this.displayedList;

                            }

                         

                         

                            @Override

                            public int getRowCount() {

                                return totalRecords.intValue();

                            }

                         

                         

                            @Override

                            public Object getWrappedData() {

                                return this;

                            }

                         

                         

                            @SuppressWarnings(QGlobalConstants.UNCHECKED)

                            @Override

                            public void setWrappedData(final Object arg0) {

                                // this.displayedList = (List<T>) arg0;

                            }

                         

                         

                        }

                         

                         

                         

                         

                        I have overridden the abstract methods in ScrollableTableDataModel, which is loadData(), and getWrappedData(). The loadData() method is never called. 

                         

                        Thanks,

                        Shriharsh 

                        • 9. Re: rich:scrollableDataTable and load on demand
                          sakthivel_k2003

                          Hi,

                          I have rich scrollable data table used in my application and it is working fine. Also when I use arrow keys to scroll the rows up and down it is working fine. But once scrolling reached to last row it wrapped to the top of the list (i.e the first row). I want to stop it in the end either it reached to last row in down arrow key or it reached to first row in the up arrow key.

                           

                          Could you please let me know how to achive this.

                           

                          Thanks,

                          Sakthi

                          • 10. Re: rich:scrollableDataTable and load on demand
                            harsh820

                            Hi Sakhi,

                             

                            Even I faced the issue of wrapping to top in the list. Were you able to get DB level paging work by extending ScrollableTableDataModel?

                            Can you please guide me where i am wrong?

                            • 11. rich:scrollableDataTable and load on demand
                              sakthivel_k2003

                              No. I am not doing paging here. The issue is changing row selection by using key bord; arrow key. Say example my data list has 18 rows which has rendered in scrollable data table. I use arrow key from my keybord to select a row up and down. If the row selection reach to the last row then if I press down arrow the first row is selected. Iwant to restrict this behaviour.

                               

                              -Sakthi