ExtJS: Reloading stores quickly


In an ExtJS application it is sometimes necessary to reload a store and the challenge is to do it quickly. If the store backs a paged grid the issue doesn’t really arise. But if the store backs a grid with many records – and specially if its a tree store backing a tree view – reloading the store efficiently is important.

This post describes and provides code for our solution to this issue.

In one of our applications it was noticed that the time to reload a store grew in proportion to some factor greater than 1 of the number of records in the store.

The application has 10 grids and backing stores. When grids are initially populated with a modest number of records it took something like 0.3 seconds. However when the stores were reloaded it could take 5 seconds to load the same data.

Grid events

The difference was obvious: the initial load of the stores takes place before they are bound to their respective grid. So being bound to a grid causes the reload performance issue.

Suspending grid events and application layouts had no measurable effect on the time to reload performance. We already had ‘loading’ checks in place to ensure that our own events were not acted upon when data is loaded.

bind and unbind

If data loads OK before the store is bound to a grid but loads slowly when its bound then the trick must be to unbind the store from the grid, load the data and then rebind it. The ExtJS grid panel includes a method called ‘reconfigure’ which allows the grid to be bound to a different store but there’s no option to just unbind the grid.

There are private methods called ‘unbindStore’ and ‘bindStore’. Even though they are marked private we tried them. While these will work, and work well in the case of a tree view and it’s corresponding tree store, they cause a problem in a flat grid. It turns out the flat grid has an override to enable the attachment of a buffered reader plugin. In itself this is not a problem. However the existence of this override means that the header’s sort property is called even when there is no store attached to the grid. This is a fatal error (and probably a bug) so it means that using the private bind and unbind methods is not an option. Since they are marked private, this is probably a good thing.

Reconfigure

That left the ‘reconfigure’ method. There is no option to pass a ‘null’ store because passing a ‘null’ store means ‘use the one you already have’. So it became necessary to pass an alternative, empty store to the reconfigure method.

To ensure the grid is always happy with the new store the store it should be based on the same model. This way the field are consistent.

One option is to define an extra set of ’empty’ stores in the application so the empty store can be applied. Another option is to hard code the ’empty’ store definition so it can be created and used at run-time. Either of these would have worked for us but we wanted something more generic. We already have 10 stores and can see additional stores being added in the futures so for us its ideal if the empty store can be generated dynamically. We already have an array of the grid panels so it will then be possible to iterate over the array, generate an empty store, reconfigure the grid to use the generated store, clear and reload the original store and finally reconfigure the grid to use the original, reloaded store.

What type of store?

In our case we use both tree panels and regular grid panels so the first step is to create the empty store by extending the correct class.

We have a handy function called ‘descendantOf’ which allows the caller to determine if an instance extends a specific type.

Ext.descendantOf=function (cls, ancestor)
{
	var name = Ext.isString(ancestor) ? ancestor : ancestor.$className;
    var cur=cls;
    while(cur !== undefined && cur.constructor)
    {
        if (cur.$className === name) return true;        
        cur = cur.superclass;
    }
    return false;
};

If you are 100% certain the stores always extend, say, ‘Ext.data.TreeStore’ then you can use something simpler like mystore.superclass.$callname === ‘ExtExt.data.TreeStore’. In the general case its good to climb the ancestor structure to be sure which is what ‘descendantOf’ does.

OK, now its possible to determine the type of the ancestor for a store its possible to write a function to create a store of the same type and structure:

cloneStore: function(store) {
	// Clone the structure of a store to create a temporary and empty copy
	var modelName = store.model.modelName;

	if (Ext.descendantOf(store, 'Ext.data.TreeStore'))
	{
		return Ext.create('Ext.data.TreeStore', {
			model: modelName,
			root: {
				expanded: true,
				children: []
			}
		});
	}
	else
	{            
	    return Ext.create('Ext.data.Store', {
	        model: modelName
	    });
	}
}

This function creates a store which extends the appropriate base type after assigning a model of the appropriate type.

Now we’re in a position to reload a store efficiently using the method reconfigure:

var originalStore = myGrid.store;
myGrid.reconfigure(this.cloneStore(originalStore));
// Code here to update originalStore 
myGrid.reconfigure(originalStore);

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Be the first to leave a comment!