Virtual Paging / Infinite Scrolling

Virtual paging allows the grid to lazy load rows from the server depending on what the scroll position is of the grid.

To enable virtual paging, set the grid property rowModelType='virtual'.

If the grid knows how many pages in total at the start, the scroll will be sized to match the entire data set despite the data set not loaded from the server.

If the grid does not know how many pages at the start, the scroll will extend automatically until the last row is reached. This feature is known in other grids as infinite scrolling.

How it Works

The following diagram is a high level overview:

The virtual model behind the grid contains a cache of pages. Each page contains a subset of the entire data set. When the grid scrolls to a position where there is no corresponding page in the cache, the virtual model uses the provided datasource (you provide the datasource) to get the rows for the requested page. In the diagram, the datasource is getting the rows from a database in a remote server.

Turning On Virtual Paging

To turn on virtual paging, you must a) set the grid property rowModelType to virtual and b) provide a datasource.

// before grid initialised
gridOptions.rowModelType = 'virtual';
gridOptions.datasource = myDataSource;

// after grid initialised, you can set or change the datasource

Aggregation and Grouping

Aggregation and grouping are not available in virtual paging. This is because to do such would require the grid knowing the entire data set, which is not possible when virtualising the pages.

Sorting & Filtering

Client side sorting & filtering does not make sense in virtual paging and is just not supported.

Server side sorting & filtering is supported.

Simple Example - No Sorting or Filtering

The example below shows virtual paging. The example makes use of infinite scrolling and caching.


Selection works on the rows in virtual pagination by using the ID of the row node. If you do not provide ID's for the row nodes, the index of the row node will be used. Using the index of the row breaks down when (server side) filtering or sorting, as these change the index of the rows. For this reason, if you do not provide your own id's, then selection is cleared if sort or filter is changed.

To provide your own id's, implement the method getRowNodeId(data), which takes the data and should return the id for the data.

gridOptions.getRowNodeId: function(item) {
    // the id can be any string, as long as it's unique within your dataset

Once you have getRowNodeId implemented, selection will persist across sorts and filters.

Example - Sorting, Filtering and Selection

The following example extends the example above by adding server side sorting, filtering and persistent selection.

Any column can be sorted by clicking the header. When this happens, the datasource is called again with the new sort options.

The columns Age, Country and Year can be filtered. When this happens, the datasource is called again with the new filtering options.

When a row is selected, the selection will remain inside the grid, even if the grid gets sorted or filtered. Notice that when the grid loads a selected row (eg select first row, scroll down so first page is removed form cache, then scroll back up again) the row is not highlighted until the row is loaded from the server. This is because the grid is waiting to see what the id is of the row to be loaded.

(note: the example below uses ag-Grid-Enterprise, this is to demonstrate the set filter with server side filtering, ag-Grid-Enterprise is not required for virtual paging)

Configuring A Bit Differently

The examples above use old style JavaScript objects for the datasource. This example turns things around slightly and creates a datasource Class. The example also just creates (makes up) data on the fly.

Loading Spinner

The examples on this page use a loading spinner to show if the row is waiting for it's data to be loaded. The grid does not provide this, rather it is a simple rendering technique used in the examples. If the data is loading, then the rowNode will be missing data, and hence all values passed to cellRenderers will be undefined. You can check for this and provide your own loading effect.

cellRenderer: function(params) {
    if (params.value !== undefined) {
        return params.value;
    } else {
        return '<img src="../images/loading.gif">'

Refer to section Cell Rendering for how to build cell renderers.

More Control via Properties and API

Virtual pagination has a cache working behind the scenes. The following properties and API are provided to allow you control of the cache.

• Property overflowSize

When infinite scrolling is active, this says how many rows beyond the current last row the scrolls should allow to scroll. For example, if 200 rows already loaded from server, and overflowSize is 50, the scroll will allow scrolling to row 250. Default is 1.

• Property maxConcurrentRequests

How many requests to hit the server with concurrently. If the max is reached, requests are queued. Default is 1, thus by default, only one request will be active at any given time.

• Property maxPagesInCache

How many pages to cache in the client. Default is no limit, so every requested page is kept. Use this if you have memory concerns, so pages least recently viewed are purged. If used, make sure you have enough pages in cache to display one whole view of the table (ie what's within the scrollable area), otherwise it won't work and an infinite loop of requesting pages will happen.

• Property paginationInitialRowCount

How many rows to initially allow the user to scroll to. This is handy if you expect large data sizes and you want the scrollbar to cover many pages before it has to start readjusting for the loading of additional data.

• API refreshVirtualPageCache()

Marks all the currently loaded page caches for reload. If you have 10 pages in the cache, all 10 will be marked for reload. The old data will continue to be displayed until the new data is loaded.

• API purgeVirtualPageCache()

Purges the cache. The grid is then told to refresh. Only the pages required to display the current data on screen are fetched (typically no more than two). The grid will display nothing while the new pages are loaded. Use this to immediately remove the old data from the user.

• API getVirtualRowCount()

The virtual row count defines how many rows the grid allows scrolling to.

• API isMaxRowFound()

The property maxRowFound is a boolean, true or false. When false, then the grid will allow scrolling beyond the virtualRowCount looking for more rows. When the last row is found, maxRowFound becomes true, and the grid will only scroll to the last available row as it has finished looking for more data.

• API setVirtualRowCount(rowCount, maxRowFound)

Sets the virtualRowCount and maxRowFound properties. The second parameter, maxRowFound, is optional and if left out, only rowCount is set. Set rowCount to adjust the height of the vertical scroll. Set maxRowFound to enable / disable searching for more rows. Use this method if you add or remove rows into the dataset and need to reset the number of rows or put the data back into 'look for data' mode.

• API getVirtualPageState()

Returns an object representing the state of the cache. This is useful for debugging and understanding how the cache is working.

Inserting / Removing Rows

• API insertItemsAtIndex(index, items)

Inserts items at the provided location inside the grid. If you use this, you MUST ensure that the data store you are sourcing from (eg the database) is also updated, as the subsequent cache page loads will need to be consistent with what is inside the grid. Doing an insert will require rows to be moved after the insert location (pushed down to make room) - this can leave blank rows in pages in the cache (if a page has to be moved down, and the previous page is not loaded for it to take rows from). If this is the case, then the page will be marked for a refresh.

Inserting rows into the virtual pagination row model allows for your grid to be out of sync with the underlying data store and hence can either cause synchronisation issues, or simply difficult code to maintain even if you get it right, especially in multi-user environments. It is strongly suggested you don't use the insertItemsAtIndex() method, rather you update the source and then refresh the cache.

• API removeItems(rowNodes)

This method is not supported by virtual pagination. It is not supported as the grid has no way of knowing the index of the rowNodes to be removed if the data is not currently loaded into the cache.

• API addItems(dataItems)

This method is not supported by virtual pagination. It is not supported as the grid has no way of knowing the end of the data dataset to be appended to if the data is not currently loaded into the cache.

• Adding / Removing Summary

Adding / removing rows directly in the grid for virtual pagination is in general bad news as you are giving a viewport and scrolling through data that resides on the server. It is better to update the data on the server and refresh the virtual page cache.

Example - Using Cache API Methods

Below demonstrates the different api methods via the buttons. The example outputs a lot of debugging items to the console because the grid property debug=true is set. The buttons are as follows:

  • Inject 1 Row @ 2 / Inject 5 Row @ 2: Inserts either one or five rows at location index 2.
  • Insert 1 Row @ 2 and Refresh: Inserts five rows at location index 2 and then gets grid to refresh.
  • Delete 10 Rows @ 3: Deletes rows from the server, then gets the grid to refresh.
  • Set Row Count to 200: Sets the virtual row count to 200. This adjusts the vertical scroll to show 200 rows. If the scroll is positioned at the end, this results in the grid automatically readjusting as it seeks ahead for the next page of data.
  • Print Rows and Max Found: Debugging method, prints virtualRowCount and maxFound to the console.
  • Jump to 500: Positions the grid so that row 500 is displayed.
  • Print Cache State: Debugging method, to see the state of the cache.
  • Set Prices High & Set Prices Low: Sets the prices ON THE SERVER SIDE to either high or low prices. This will not impact the grid until after a page cache is loaded. Use these buttons then then further test the refresh and purge methods.
  • Refresh Cache: Calls for the cache to be refreshed.
  • Purge Cache: Calls for the cache to be purged.
The example also makes each Honda row bold - demonstrating that the callbacks getRowStyle and getRowClass get called after the data is set as well as when the row is created (when the data may not yet be available).