I have a serious performance problem updating records through a hasMany relation. (using sencha extjs 4.0)
Suppose we have a model with a hasMany relation. I'll rewrite my actual stuff in a simplified model based on sports teams and their players.
Ext.define('project.model.Team', {
extend: 'Ext.data.Model'
, fields: [
{name: 'id', type: 'int'}
, {name: 'name', type: 'string'}
]
, hasMany: [
{ model: 'project.model.Player', name: 'players' }
]
});
Ext.define('project.model.Player', {
extend: 'Ext.data.Model'
, fields: [
{name: 'id', type: 'int'}
, {name: 'team_id', type: 'int'}
, {name: 'name', type: 'string'}
, {name: 'attribute', type: 'int'}
]
, associations: [
{ type: 'belongsTo', model: 'project.model.Team', foreignKey: 'team_id', primaryKey: 'id' }
]
});
Suppose there are a lot of teams and a lot of players, but we want to update something on each player in a specific team. Maybe the team played a game, so we add one game played. Details not important.
This was the working code. It wasn't a problem until someone had a "team" with a lot of "players" in an already pretty large database. This loop takes forever at a fairly modest size. I think set is triggering extra checks on every iteration, instead of just setDirty.
teamStore.getById(record.get('item_id')).players().each(function(player) {
var player_instance = exceptionStore.getById(exception.get('id'));
player_instance.set('attribute', value);
});
playerStore.sync();
This runs really quickly, sets the attribute on the reference object, and does NOTHING.
teamStore.getById(record.get('item_id')).players().each(function(player) {
player.set('attribute', value);
});
teamStore.sync();
playerStore.sync();
I think that code doesn't work because the players are just some sort of reference with no info other than their id in the player store, but they are not actually connected to the store in any way.
Is there some faster way to get the players associated with a team, and actually do something?
You need to put the store in batch update mode with suspendEvents, so it queues up all the changes, rather than try to do all the internal bookkeeping one-at-a-time during your loop. Once you are done changing it, let it catch up in resumeEvents.
https://docs.sencha.com/extjs/4.0.7/#!/api/Ext.data.Store-method-suspendEvents
playerStore.suspendEvents();
playerStore.query('team_id', team_id).each(function(player){
player.set('attribute', value);
});
playerStore.resumeEvents();
playerStore.sync();
Related
I am looking to create some logic for JSON data retrieved from a server request. As you can see from the raw data below, it is received in a particular format.
There is a "balances" entry which, in this case, has 5 different sub-values, of which names can vary dependent upon a given user.
For example:
Barry has 5 bank accounts, each with different balances.
Melissa has 2 bank accounts, each with different balances.
Melissa's bank accounts are different to Barry's bank accounts, and vise versa.
The Balance ID numbers that are assigned, may not necessarily match both Barry and Melissa.
In the Ext JS grid, the column headings which need to be displayed, must adjust to both Barry and Melissa's individual bank account balances.
Barry's JSON Data:
{
"firstName": "Foo",
"lastName": "Bar",
"balances":
{
Natwest: 9,
BankofScotland: 2,
Lloyds: 40,
Halifax: 89,
Lords: 12
},
}
Melissa's JSON Data:
{
"firstName": "Melissa",
"lastName": "Bar",
"balances":
{
DifferentNatwest: 10,
DiffferentBankofScotland: 45
},
}
At the moment, I only have one mapping in my store/model, called "balances" which only takes one value:
Store/Model definitions:
fields: ['firstName', 'lastName', 'balances']
So, obviously the following issue occurs when the grid is generated, as more than one value is being passed:
Results:
The Question:
Does anyone know what I can do to get the columns to dynamically generate in this Ext JS grid, dependant upon the JSON data being received for this balances information?
What you need is to create the grid columns and store for it dynamically.
I don't know if there is some more "ExtJS" way (simpler, automatic creation) but I would do a straightforward solution.
1# Solution - with multiple columns
Get the data, get the JSON object
Based on the JSON object create the new grid columns
Based on the JSON object create the new store with correct records (I
don't think there is model which can have object in it - you need to transofrm it)
Create the grid and add the new store and columns to it.
Here is the code snippet with the most important part:
for (var key in d.balances) {
bankAcountsColumns.push({
xtype: 'gridcolumn',
dataIndex: key,
text: key
});
transformData[key] = d.balances[key];
fields.push(key);
}
var myCustomColumns = [{
xtype: 'gridcolumn',
dataIndex: 'firstName',
text: 'Name'
}, {
xtype: 'gridcolumn',
dataIndex: 'lastName',
text: 'LastName '
}, {
xtype: 'gridcolumn',
text: 'Bank accounts',
columns: bankAcountsColumns
}]
Ext.create('MyApp.view.MyGridPanel', {
renderTo: Ext.getBody(),
columns: myCustomColumns,
store: {
fields: fields,
data: transformData
}
});
Here is working fiddle example:
https://fiddle.sencha.com/#view/editor&fiddle/1l5j
2# Solution - one column
If you want to have only one column with all the balances, you would need templatecolumn
In there I would create template for 5 items (or it can be again created dynamically):
tpl:'{bank1name} {bank1value} {bank2name} {bank2value} ...'
And than you would have to dynamically create the data for the new store.
data:[
{ firstName: "Foo", lastName:"Scott", bank1name: "Natwest", bank1value: "9" ... },
{ firstname: "Dwight", lastName: "Bar", ... bank5name: "" ... }
]
Possible Duplicate: How to set url and root dynamically in extjs
Hi there, I have a simple memoryStore. If I tried not to declare its proxy during the Ext.Define, I am unable to retrieve the proper data root later on, even if I do set the proxy. Am I doing something wrong?
Here's a test case:
var store = Ext.create('Ext.data.Store', {
storeId: 'JailNames',
autoLoad: true,
fields: [
{
name: 'name',
type: 'string'
},
],
data: {
data_regionI: [
{name: "Jail 1"},
{name: "Jail 2"},
{name: "Jail 3"},
],
data_regionII: [
{name: "Jail 4"},
{name: "Jail 5"},
{name: "Jail 6"},
],
},
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'data_regionI'
}
}
})
store.setProxy({
type: 'memory',
reader: {
type: 'json',
root: 'data_regionII'
}
} )
store.load();
store.getAt(0).raw //still returns Jail 1
Looking through store.getProxy().reader.root I get the data_regionII as a root. Why?
Thanks in advance
If I copy your code into a sencha fiddle of Version 4.1.1, it throws an Uncaught TypeError: Cannot read property 'raw' of undefined, which is what I expected, because the store shouldn't contain any records at all after the call to load().
There are many problems in your understanding what a store does and what a proxy does:
A normal store's load function will tell the proxy to fetch the data, tell the reader to make records from it, and load it into the data property of your store, overwriting(!) the data you have defined at initialization.
But a memory store's load function isn't intended to do anything at all, and isn't intended to be used at all.
A memory store isn't intended to hold more than one store content at the same time. (you can, however, store the unused contents in an unused(!) property of the store's JavaScript object).
A store, no matter which proxy, does not require autoLoad:true to load the content of data into the store - the content of data is automatically used as the default data of the store after initialization.
That said, it's still possible to achieve what you want with just a few lines of code. You don't even have to create all the functions I only made for readability:
var store = Ext.create('Ext.data.Store', {
storeId: 'JailNames',
fields: [
{
name: 'name',
type: 'string'
},
],
myData: { // custom property!
data_regionI: [
{name: "Jail 1"},
{name: "Jail 2"},
{name: "Jail 3"},
],
data_regionII: [
{name: "Jail 4"},
{name: "Jail 5"},
{name: "Jail 6"},
],
},
loadRegion1:function() {
this.loadRegion("data_regionI");
},
loadRegion2:function() {
this.loadRegion("data_regionII");
},
loadRegion:function(rootProperty) { // custom function for better readability
this.loadRawData(this.myData[rootProperty]); // load data without proxy, but with reader!
},
proxy: {
type: 'memory',
reader: {
type: 'json'
}
}
});
store.loadRegion1();
console.log(store.getAt(0).get("name")); //returns Jail 1
store.loadRegion2();
console.log(store.getAt(0).get("name")); //returns Jail 4
I asked an ExtJS question a few days ago, and as a side note I also asked how I could connect my two models. Main answer got answered, but I still couldn't figure out my other problem, so I am opening a new question for it.
It might be a silly problem again, but here it is:
I get a JSON from the server, that looks like this:
{
"success": true,
"result": {
"publishers": [
{
"id": "009999",
"type": "ABC",
"isReceipient": false,
"description": "XYZ"
},
{
"id": 45,
"type": "ABC",
"isReceipient": true,
"description": "XYZ"
},
{
"id": 45,
"type": "ABC",
"isReceipient": false,
"description": ""
}
],
"notes": [
{
"publisherId": "009999",
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
},
{
"publisherId": "46",
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
},
{
"publisherId": 45,
"text": "asdasd",
"created": "2014-02-23T18:24:06.074Z"
}
]
}
}
So I get two arrays, publishers and notes. I have two model for that, I load them in the models by the controller using loadRawData(), it works, I got all my publishers and notes in the store. (They both have a store - Publishers and Notes). But then I need to use the publisherId in notes to display publishers description. I tried a lot of things I could find using google and sancha docs: associations, hasmany, hasone, belongsto and creating a third store consisting of the two aggregated model. Nothing worked so far.
What I want is to have a store that has every notes, plus all notes have the publisher info.
I'll copy my two models below, you can see there, commented out what I have been trying. I also tried changing ID's, names etc., so variations of these. But I could never get the Notes to have the publisher's info.
Ext.define('MA.model.Note', {
extend: 'Ext.data.Model',
fields: [
'publisherId',
'text' ,
//hasone publisher
{
name: 'created',
type: 'date',
dateFormat: 'c'//'d-M-Y H:i:s' //"2014-02-23T18:24:06.074Z" needed: "02/23 18:24"
}
]
// hasOne: [
// {
// name: 'publisher',
// model: 'Publisher',
// associationKey: 'publisherId'
// }
// ],
// associations: [
// {
// type: 'hasOne',
// model: 'Publisher',
// primaryKey: 'id',
// foreignKey: 'publisherId'
// }
// ]
// associations : [
// {
// type : 'hasOne',
// model : 'MA.model.Publisher',
// getterName : 'getPublisher',
// associatedName : 'User',
// associationKey : 'User'
// },
// {
// type : 'belongsTo',
// model : 'MA.model.Publisher',
// getterName : 'getPublisher',
// associatedName : 'Publisher',
// associationKey : 'publisherId'
// }
// ]
// belongsTo: [
// {
// model: 'MA.model.Publisher',
// name: 'Note',
// primaryKey: 'publisherId',
// foreignKey: 'id',
// // foreignStore: 'Publishers'
// }
// ]
});
Publisher:
Ext.define('MA.model.Publisher', {
extend: 'Ext.data.Model',
idProperty: 'id',
fields: [
'id',
'type' ,
{
name:'isReceipient',
type:'boolean'
},
'description'
]
// hasMany: [
// {
// model: 'MA.model.Note',
// name: 'Note',
// primaryKey: 'id',
// foreignKey: 'publisherId',
// // foreignStore: 'Notes'
// }
// ],
});
Am I even on the right track? Should I use associations? I couldn't even really get the difference between associations and hasMan/One/belongTo properties, I guess there isn't any really, just the way you declare it.
Edit: My idea is to have a DataView class, that has a store which holds the notes and the corresponding publisher to the notes. I have a main panel:
items: [
{
xtype: 'create-note-panel',
flex: 1
},
{
xtype: 'notes-panel',
store: 'Notes',
flex: 1
}
]
And my notes-panel looks something like this:
Ext.define('MA.view.sections.notes.NotesPanel' ,{
extend: 'Ext.DataView',
alias: 'widget.notes-panel',
// deferInitialRefresh: true,
itemSelector: 'div.notes-list',
tpl: new Ext.XTemplate(
'<div class="notes-list">',
'<tpl for=".">',
'<p>{created}, by {publisherId}</p>',
'<p>{text}</p>',
'<hr />',
'</tpl>',
'</div>'
),
emptyText: 'No data available',
initComponent: function() {
var me = this,
publisherStore = Ext.data.StoreManager.lookup('Publishers');
//me.addEvents( //just messing here, trying stuff
// 'user-offer-activities'
//);
me.callParent(arguments);
}
//renderTo: Ext.getBody()
})
;
Notice the publisherId in the template. I need the publisher description there. I didn't want to use grid, as this DataView seemed a pretty good solution, I thought joining two stores would be easy, I just couldn't figure it out yet :(
I have created a fiddle that results in what you are after (displaying data from both models in the View).
It is a bit of a longwinded approach though, because of the way the tpl works you don't have access to the Model, just the data within it. So I created a function on the tpl that gets the record we're interested in from the model based on the publisherId.
https://fiddle.sencha.com/#fiddle/o9o
Note: I created the fiddle without using any associations between the models, but you could probably create a hasOne association from the Notes to Publisher with a foreignKey linking the id and publisherId in the respective models (though I still don't think this would enable you to refer to the members directly in the tpl).
I have a TreeStore, which works fine, but every node has some nested data, so I made a new model for it and used the hasMany association. But now the store loads nothing anymore. When I look into the records in the load event, they're empty, but the browser tells me the Ajax request delivered everything like before.
This is what the node data looks like, when it comes from the server:
{
"path": "KEY_518693",
"name": "KEY_518693",
"data": [
{
"branch": "KEY_518693",
"primnav": "ETC",
"X": 29261,
"Y": 96492
},
...
],
"children": [ ... ],
...
}
These are my model definitions:
TreeNode:
{
extend : 'Ext.data.Model',
requires: [
'DataRecord',
'Ext.data.association.HasMany'
],
fields : [
{ name: 'id' , type: 'string', mapping: 'path' },
{ name: 'text', type: 'string', mapping: 'name' },
...
],
hasMany : {
model: 'DataRecord',
name : 'data'
}
DataRecord:
{
extend: 'Ext.data.Model',
fields: [
{ name: 'branch' , type: 'string'},
{ name: 'primnav', type: 'string' },
{ name: 'X' , type: 'int' },
{ name: 'Y' , type: 'int' }
]
}
When I remove the association, the tree loads without problems. When I add data to the fields it gets parsed into the tree, but as "raw" object and not as model instance.
Please note that DataRecord has no field called treenode_id - so your hasMany association isn't complete. See docs for more info.
My approach had 2 problems.
The name of the association should not be 'data' or the records wont get loaded at all.
The primaryKey of the association has to be set right, in my case 'branch' was right
I'm getting an error like I added below while using static data with memory proxy.
Can someone show me my mistake or missing part?
Thanks in advance.
me.model is undefined
me.setProxy(me.proxy || me.model.getProxy());
My model definition:
Ext.define(appName + '.model.Country', { extend: 'Ext.data.Model',
fields: [
{type: 'string', name: 'abbr'},
{type: 'string', name: 'name'},
{type: 'string', name: 'slogan'}
]
});
And here's my store definition:
// The data for all states
var data = {
states : [
{'abbr':'AL','name':'Alabama','slogan':'The Heart of Dixie'},
{'abbr':'AK','name':'Alaska','slogan':'The Land of the Midnight Sun'}
]
};
Ext.define(appName + '.store.Countries', {
extend : 'Ext.data.Store',
model : appName + '.model.Country',
data : data,
proxy: {
type: 'memory',
reader: {
type: 'json',
root: 'states'
}
}
});
You might want to check if the model file is actually loaded and available for usage. When dealing with a large number of files, ExtJS (I have encountered this while working with 4.2.1) has problems with ordering them.
A quick fix is using requires: in the application definition:
Ext.application({
name: 'APP',
appFolder: 'application',
controllers: [
...
],
requires: ['APP.model.examples', 'APP.model.other' ...],
...
});
If this helps, I have written more on a PHP solution here:
Solution for ExtJS 4 me.model is undefined error
Did you try to create the store explicitly and specify it in the config of the container?
For example:
var store = Ext.create(appName + '.store.Countries');
Ext.create('Your Component', {
...
store: store,
...
});