True to my promise over the years, anytime it takes me significant mental and time resources to discover how to accomplish a development task that I think others might find useful, I will share the process.
I’ve got an app in which users need access to a dataset that is larger than 10k records. Pagination to the rescue (plus a search feature which I covered in my Replacing data in Sencha ExtJS grid store article). I’m giving the users the option of paging through the results 100 at a time if they so wish.
It ended up being simple to accomplish after scouring through the documentation and looking at related articles throughout the Web.
First you need to have a script that returns sets of 100 results. In my case, I have a ColdFusion Component that does a CFQUERY on the table. I simply pass in the number from which I want to start my resultset and it returns the next 100 rows. Convert your query to a JSON string using serializeJSON() in ColdFusion, or any method you wish.
Next, I created a ColdFusion page that accepts a start parameter – which is sent from the PagingBar object in ExtJS – and passes that along to the CFC. The resulting JSON string simply gets output.
<cfsetting enablecfoutputonly="true">
<cfparam default="" name="start">
<cfscript>
providers = application.widget.framework.getBean('Insurance').listInsurance(startRecord=start);
writeOutput(insurers);
</cfscript>
<cfsetting enablecfoutputonly="false">
Next, create a Store object with this page as its proxy, and a JsonReader to parse the results.
var Insurer = Ext.data.Record.create([
{name: 'col1', type: 'int'},
{name: 'col2', type: 'string'},
{name: 'col3', type: 'string'},
{name: 'col4', type: 'int'}
]);
var InsurerStore = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url: 'liveQueries/insurance.cfm'}),
reader: new Ext.data.JsonReader({ root: 'data', totalProperty:'totalRecords' }, Insurer)
});
Now you can create your PagingBar object with the Store object you created as its datastore.
var PagingBar = new Ext.PagingToolbar({
pageSize: 100,
store: InsurerStore,
displayInfo: true,
displayMsg: 'Displaying topics {0} - {1} of {2}',
emptyMsg: 'No insurers to display'
});
Lastly, attach your PagingBar to a DataGrid object with the bbar property. You also have the option of performing an initial search to show the first 100 record to the user by default. To do this, just call the load() event on your Store (shown below).
var InsurerGrid = new Ext.grid.GridPanel({
store : InsurerStore,
sm : new Ext.grid.RowSelectionModel({singleSelect:true}),
cm : InsurerColumnModel,
renderTo : 'insurer-grid',
frame : true,
bbar : PagingBar
});
InsurerStore.load({params:{start:0, limit:100}});
This is mainly for my own information retrieval, but perhaps it might be useful to another Javascript developer out there who is using ExtJS.
I’ve got a list of several thousand insurance plans I need to display to the users of one of my applications. I originally implemented a standard Ext.PagingToolbar to add to the footer of my data grid, but I’ve never been a fan of that navigation pattern, and I suspect that my users wouldn’t be either.
I then thought of sites like Google Reader that automatically retrieves the next set of records as soon as it detects that you’ve scrolled to a certain place in the reading element. A quick trip to my friend Google uncovered an article on The Code Project by Jason Witty that provided an example of just this technique.
A quick adaptation to my application provided a working example:
var emptyData;
// Set default scroll variables.
var currentpos = 0;
var currentpage = 1;
// Set constants.
//itemsperpage: number of records that appear on each page of content.
var itemsperpage = 100;
// Number of pages of content to fetch when offset reached.
var pagestofetch = 1;
// Number of pixel interval to fire handler request. You'll need to adjust this value
// depending on the location and size of your data grid
var scrolloffset = 1800;
var scrolloffsetinterval = 1800;
// Create the column model to display only the data needed for the users
var InsurerColumnModel = new Ext.grid.ColumnModel([{
id: 'col1',
header: 'Abbreviation',
dataIndex: 'col1',
width: 120
},{
id: 'col2',
header: 'Name',
dataIndex: 'col2',
width: 240
},{
id: 'col3',
header: 'Group',
dataIndex: 'col3',
width: 120
}
]);
// Create a JSON Store to hold the data elements
var ScrollingProviderStore = new Ext.data.JsonStore({
data: emptyData,
root: 'data',
fields: Insurer
});
// Create the grid to display each insurer
var InsurerGrid = new Ext.grid.GridPanel({
store: ScrollingProviderStore,
sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
cm: InsurerColumnModel,
renderTo: 'insurer-display-grid',
stripeRows: true,
width:'auto',
height:350,
autoScroll: true,
autoExpandColumn:'col2',
title:'Insurance Plans',
frame:true
});
// Create an event listener for when the user scrolls the grid
InsurerGrid.addListener('bodyscroll',scrollListener);
// Fires when grid is scrolled
function scrollListener(scrollLeft, scrollTop){
// Only handle scroll downs past highest position.
if ( scrollTop > currentpos )
{
// Check if we should get more data
if ( scrollTop > scrolloffset )
{
// Store current grid scroll state.
var state = InsurerGrid.getView().getScrollState();
// Adjust scroll offset
scrolloffset = scrollTop + scrolloffsetinterval;
// Adjust current page
currentpage = currentpage + 1;
$.AjaxCFC({
url: 'ajaxCFC/ajax.cfc',
factory: 'application.framework',
bean: 'Insurance',
method: 'listProviders',
data: { 'start': currentpage },
timeout:30000,
useDefaultErrorHandler: false,
success: function(result) {
var insurers = eval('(' + result + ')');
ScrollingProviderStore.loadData(insurers,true);
// Restore the scrollbar state back to where the user
// triggered the load.
InsurerGrid.getView().restoreScroll(state);
},
error: function(results) {
Ext.MessageBox.alert('Failed', 'Failed to retrieve insurers. Please try again.');
}
});
}
currentpos=scrollTop;
}
};