PDA

View Full Version : Overriding proxy in a single instance of a store overrides it for all instances



jonjanisch
21 Nov 2013, 8:39 AM
We have a standard store definition with a rest proxy:


Ext.define('LP.store.MyStore', {
extend: 'Ext.data.Store',
requires: 'LP.model.MyModel',
model: 'LP.model.MyModel',
storeId: 'MyStore',
proxy: {
type: 'rest',
url: 'api/someUrl',
reader: {
type: 'json',
root: 'data'
}
}
});



It's being used in a combobox like this:

xtype: 'combo',
store: 'MyStore',
queryMode: 'local'

And there's 1-2 lines of code which loads this store with some URL params in this views controller.

In another class, I had the need to use this store, but I wanted a different URL. So I wrote some code like this:

var myStore = Ext.create('LP.store.MyStore', {
proxy: {
type: 'rest',
url: 'api/differentURL',
reader: {
type: 'json',
root: 'data'
}
}
});

When I navigate back to the screen with the original combobox, I was surprised to see that the original store's proxy was also changed (to use "differentURL")! My whole understanding of ExtJS stores seems to have been invalid. I understand if I specify a storeId it's registered in the storemanager. But I always assumed if I do an Ext.create I'm getting a completely separate Ext.data.Store and it won't affect any other store (even if it's registered in the store manager). So for example, the combobox uses the singleton instance whereas "myStore" is a new instance and all data is independent.

Now, I should have simply created a new Ext.data.Store and reused the same model (instead of reusing the store class) and I will make that change. But still, this is a bit worrying as now I have to search through a large code base and find any place where we're doing Ext.create(someStore).

Can someone explain in a little more detail how this works? How exactly is the proxy copied to all store instances?

troseberry
21 Nov 2013, 10:10 AM
Because of the nature of the stores any component that is bound to the store by the same storeId will reflect any changes that are made to the store.

please see the following link from the Sencha Blogs
https://www.sencha.com/blog/top-sencha-support-tips/
(https://www.sencha.com/blog/top-sencha-support-tips/)look at the section "One Store to Rule Them All (ExtJs)"

jonjanisch
21 Nov 2013, 10:37 AM
Yes I've seen that post you linked to and I understand that comboboxes that refer to the same store by storeId get the same instance, but I don't think that's the issue I'm describing (it may be related). Also, neither that post nor the documentation really explains this topic well.For instance, simply initializing the variable "myStore" without ever even using it anywhere in the code will change the proxy for all stores.

jonjanisch
21 Nov 2013, 10:52 AM
I looked at the source code for AbstractStore and I see what's happening.

Even though I'm creating a new instance, it will still register this second instance with the StoreManager:

if (me.storeId) {
Ext.data.StoreManager.register(me);
}

So now StoreManager has two entries for the same storeId, but the lookup(storeId) method will return the last store registered with this ID (since it internally uses a map). So in summary, anywhere where we have Ext.create(...) with a store that has a storeId, it's a bug in our application.

troseberry
21 Nov 2013, 11:28 AM
Try the code below. I just setup a simple example of two combos with a single store instance.



Ext.define('State', {
extend: 'Ext.data.Model',
fields: [
{type: 'string', name: 'abbr'},
{type: 'string', name: 'name'},
{type: 'string', name: 'slogan'}
]
});




// The data for all states
var states = [
{"abbr":"AL","name":"Alabama","slogan":"The Heart of Dixie"},
{"abbr":"NM","name":"New Mexico","slogan":"Land of Enchantment"},
{"abbr":"OH","name":"Ohio","slogan":"The Heart of it All"},
{"abbr":"OK","name":"Oklahoma","slogan":"Oklahoma is OK"},
{"abbr":"WA","name":"Washington","slogan":"Green Tree State"},
{"abbr":"WV","name":"West Virginia","slogan":"Mountain State"},
{"abbr":"WI","name":"Wisconsin","slogan":"America's Dairyland"},
{"abbr":"WY","name":"Wyoming","slogan":"Like No Place on Earth"}
];


Ext.define('StatesStore', {
extend: 'Ext.data.Store',
storeId: 'StatesStore',
alias: 'store.states',
model: 'State',
data: states
});




Ext.onReady(function() {

var simpleCombo1 = Ext.create('Ext.form.field.ComboBox', {
fieldLabel: 'Filter',
renderTo: 'simpleCombo1',
displayField: 'name',
width: 320,
labelWidth: 130,
store: {type: 'states'},
queryMode: 'local',
typeAhead: true
});
var simpleCombo2 = Ext.create('Ext.form.field.ComboBox', {
fieldLabel: 'UnFiltered',
renderTo: 'simpleCombo2',
displayField: 'name',
width: 320,
labelWidth: 130,
store: {type:'states'},
queryMode: 'local',
typeAhead: true
});

var button = Ext.create('Ext.button.Button', {
text: 'Filter Store',
renderTo: 'buttonFilter',
handler: function () {
simpleCombo1.getStore().filter('name', "Ohio");
simpleCombo2.getStore().filter('name', "Wyoming");
}
});
var clear = Ext.create('Ext.button.Button', {
text: 'Clear',
renderTo: 'clearFilter',
handler: function () {
simpleCombo1.getStore().clearFilter();
}
});

});

troseberry
21 Nov 2013, 11:36 AM
You can always omit the storeId or specify it inline when you create the new store on the combo

store: {storeId: 'MyStore1', type: 'states'}

it really creates 2 separate stores. You just get the convenience of inheriting the base store class.

I personally uses the same model with proxy defined and then create seperate stores to use. If I need to dynamically change the proxy config then I can reference it from the store this way it doesn't mess with other components bound store data