6

How would I go about waiting for multiple stores to load? I have a case where I need to do some work only when two different stores are loaded, so using the store.on("load", fn) of one store is not good enough.

Patrick
  • 2,672
  • 3
  • 31
  • 47
Alex Ivasyuv
  • 8,585
  • 17
  • 72
  • 90
  • Related to http://stackoverflow.com/questions/11003605/how-to-wait-until-all-stores-are-loaded-in-extjs – Patrick Oct 29 '12 at 19:56

5 Answers5

8

We use this when there are several stores to wait on:

Ext.define('Ext.ux.StoreLoadCoordinator', {
mixins: {
    observable: 'Ext.util.Observable'
},
resetStoreLoadStates: function() {
    this.storeLoadStates = {};              

    Ext.each(this.stores, function(storeId) {
        this.storeLoadStates[storeId] = false;
    }, this);       
},    
isLoadingComplete: function() {
    for (var i=0; i<this.stores.length; i++) {
        var key = this.stores[i];

        if (this.storeLoadStates[key]==false) {
            return false;
        }
    }

    return true;        
},    
onStoreLoad: function(store, records, successful, eOpts, storeName) {
    this.storeLoadStates[store.storeId] = true;

    if (this.isLoadingComplete()==true) {
        this.fireEvent('load');
        this.resetStoreLoadStates();
    }
},    
constructor: function (config) {
    this.mixins.observable.constructor.call(this, config);

    this.resetStoreLoadStates();

    Ext.each(this.stores, function(storeId) {
        var store = Ext.StoreManager.lookup(storeId);

        store.on('load', Ext.bind(this.onStoreLoad, this, [storeId], true));
    }, this);

    this.addEvents(
        'load'            
    );
}});

To use it, pass in an array of the relevant storeIds:

var store1 =  Ext.create('Ext.data.Store', {
    storeId: 'Store1',
    .... (rest of store config)
}});        

var store2 =  Ext.create('Ext.data.Store', {
    storeId: 'Store2',
    .... (rest of store config)
}});        


var coordinatior = Ext.create('Ext.ux.StoreLoadCoordinator', {
    stores: ['Store1', 'Store2'],
    listeners: {
        load: function() {
           // Do post-load work
        }
    }
});         

This will give you a single load event to handle for multiple stores. Please note that this requires Ext 4.x or later.

McCroskey
  • 1,091
  • 1
  • 11
  • 21
4

I have had some projects that required several stores to be loaded before working with the data. I added a callback on them to check if each item in the Ext.data.StoreManager was loaded and if so it would do what I needed with the data.

To add stores to the StoreManager you just have to give it a storeId in its config, something like this:

var store1 = Ext.create('Ext.data.Store', {
    model: myModel,
    storeId: 'store1', //<-- adds this to Ext.data.StoreManager
    proxy: {
        type: 'ajax', 
        url: 'url...',
        reader: 'json'
    },
    autoLoad: {
        callback: initData
    }
});

var store2 = Ext.create('Ext.data.Store', {
    model: myModel,
    storeId: 'store2',
    proxy: {
        type: 'ajax', 
        url: 'url...',
        reader: 'json'
    },
    autoLoad: {
        callback: initData
    }
});

// Initialize store dependencies when all stores are loaded
function initData() {
    var loaded;
    Ext.data.StoreManager.each( function(store) {
        loaded = !store.isLoading();       
        return loaded;
    });
    if(loaded) {
        // do stuff with the data
    }
}
egerardus
  • 11,316
  • 12
  • 80
  • 123
  • 2
    Not sure, but I think the downvote was due to `loaded = !store.isLoading();` which should be `loaded &= !store.isLoading();`. See this [jsfiddle](http://jsfiddle.net/JHMzp/1). There should be a comment in any case, to help us all :) – aletzo Sep 06 '12 at 11:44
2

Usually I avoid the problem because I strive to minimize client/server roundtrips and improve performance: I set autoLoad to false on the store, then do an explicit ajax request that gets the data for all stores at once, then use store.loadData() to directly set it to each store. The downside is of course that it requires more code and results in tighter coupling.

eekboom
  • 5,551
  • 1
  • 30
  • 39
1

I use chain loading stores to solve this.

Cons: slower than it should be.

chainStoreLoad :function (stores, lastCall, idx)
{
    if (idx === stores.length) {
        if ("function" === typeof lastCall) {
            lastCall.call ();
        }
        return;
    }
    stores[idx].load (function (r,o,s) {
        Jx.chainStoreLoad (stores, lastCall, idx + 1);
    });
}

Example:

Jx.chainStoreLoad (
    [
        this.storeAssetType
    ,   this.storeAssetProcurement
    ,   this.storeAssetStatus
    ,   this.storeAssetLocation
    ,   this.storeSystemUser
    ]
,   function ()
    {
        self.panel.doRefresh (perm);
    }
,   0);
sulhan
  • 11
  • 1
1

i think you can add load listeners for both stores, and check if the other one finished ...

this.getStore('store1').on('load',function(store){
   if (this.getStore('store2').isLoading() == false ){
     // callMethod to perform action ....
   }
},this);

this.getStore('store2').on('load',function(store){
   if (this.getStore('store1').isLoading() == false ){
      // callMethod to perform action....
   }
},this);

I think this way it will call the method only when they are both loaded assuming that you know the request for load has been made for both.

nscrob
  • 4,483
  • 1
  • 20
  • 24