-
23 Apr 2012 9:44 AM #1
[4.1 RC3] CopyFrom model not copy hasmany
[4.1 RC3] CopyFrom model not copy hasmany
REQUIRED INFORMATION
Ext version tested:
- Ext 4.1 GA
Browser versions tested against:
- IE9
- FF11
Description:
- When I save a model with a hasmany property, the model is updated on the server correctly. But when the model is interpreted by the EXTJS the child relations are not updated correctly.
After some debugging, I found that the Ext.data.Operation method uses the commitRecords method to copy the server record to the client record.
The server record is OK, but the client record doesn´t have its child’s updated after the copyFrom method.
Steps to reproduce the problem:
- Run test case
The result that was expected:
- Orginal and Copy is equal
The result that occurs instead:
- Original and Copy is different
Test Case:
Code:<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1" /> <link rel="stylesheet" type="text/css" href="http://dbs-gmf-service/extjs/ext-4.1/resources/css/ext-all-gray.css" /> </head> <body> </body> <script type="text/javascript" src="http://dbs-gmf-service/extjs/ext-4.1/ext-all-debug.js"></script> <script type="text/javascript"> Ext.onReady(function(){ Ext.define('GMF.model.DTODeTelefone', { extend: 'Ext.data.Model' ,fields: [ { name: 'Id'} ,{ name: 'Telefone'} ] ,proxy: { type: 'rest', url: '/Apresentacao/Telefone' , api: { read: '/Apresentacao/Telefone/', create: '/Apresentacao/Telefone/Novo', update: '/Apresentacao/Telefone/Editar', destroy: '/Apresentacao/Telefone/Excluir' } , actionMethods: { create: 'POST', read: 'GET', update: 'POST', destroy: 'POST' } , reader: { root: 'Entidade' } } ,idProperty : 'Id' }); Ext.define('GMF.model.DTODePessoaTelefone', { extend: 'Ext.data.Model' ,fields: [ { name: 'ID'} ,{ name: 'Nome'} ,{ name: 'NomeDaMae'} ,{ name: 'CPF'} ] ,proxy: { type: 'rest', url: '/Apresentacao/PessoaTelefone' , api: { read: '/Apresentacao/PessoaTelefone/', update: '/Apresentacao/PessoaTelefone/Editar' } , actionMethods: { create: 'POST', read: 'GET', update: 'POST', destroy: 'POST' } , reader: { root: 'Entidade' } } , hasMany: [ { model: 'GMF.model.DTODeTelefone', name: 'Telefones' } ] , idProperty: 'ID' }); var pessoaTelefone = Ext.create('GMF.model.DTODePessoaTelefone'); var telefone = Ext.create('GMF.model.DTODeTelefone'); pessoaTelefone.Telefones().add(telefone); var pessoaTelefoneCopy = Ext.create('GMF.model.DTODePessoaTelefone'); pessoaTelefoneCopy.copyFrom(pessoaTelefone); document.body.innerHTML = 'Copy: ' + pessoaTelefoneCopy.Telefones().count(); document.body.innerHTML += '<br/>'; document.body.innerHTML +='Original: ' + pessoaTelefone.Telefones().count(); }); </script>
HELPFUL INFORMATION
Debugging already done:
- commitResults in Ext.data.Operation
Operating System:
- Win7
-
24 Apr 2012 4:30 AM #2
-
2 Jul 2012 2:03 PM #3
UPDATE: I am using Ext 4.1.0
I'm seeing this same issue with my belongsTo associations.
For me, I am just initially saving the record. The server returns the object with the associations pre-populated, but the client record doesn't pick them up.
The original poster has this issue nailed. The Ext.data.Model.copyFrom method doesn't do anything with associations.
Can we confirm this as a bug or feature request? I'll see if I can write a hack that fixes my issue, and if worthy, I'll post it here.
-
2 Jul 2012 2:15 PM #4
This seems to work for my situation. I haven't tested with all associations or all operations:
Code:Ext.data.Model.override({ /** * @Override * Copies association data after calling super.copyFrom */ copyFrom: function(sourceRecord) { this.callParent(arguments); var associations = this.associations.items, i, association, mine, theirs, instanceName; for (i=0; i<associations.length; i++) { association = associations[i]; instanceName = association.instanceName; theirs = sourceRecord[instanceName]; if (theirs) { mine = this[instanceName]; if (mine) { mine.copyFrom(theirs); } else { this[instanceName] = theirs; } } } } });
-
4 Apr 2013 7:01 AM #5
Here's a patch for HasMany associations, starting from the JamaSoftware-Sencha one.
It first delete the associated store, then it creates a new one and finally it populates the store with server data.Code:Ext.data.Model.override({ /** * @Override * Copies association data after calling super.copyFrom */ copyFrom: function(sourceRecord) { this.callParent(arguments); var associations = this.associations.items, len = associations.length, i, association, storeName, targetStore, sourceStore; for (i=0; i<associations.length; i++) { association = associations[i]; if(association.type == 'hasMany') { storeName = association.storeName; sourceStore = sourceRecord[storeName]; if(sourceStore) { delete this[storeName]; association.createStore().call(this); this[storeName].loadRecords(sourceStore.getRange()); } } } } });
The creation of a new store seems necessary when creating new records because the store has a filter that need to be updated with server data.
Initially, the hasManyStore has a filter {property: pkey, value: null}
After the record has been saved, it will have a pkey, so the filter must be updated.
So this line
association.createStore().call(this);
avoid the duplication of
Ext.data.association.HasMany#createStore
Hope this helps
-
24 Apr 2013 12:08 AM #6
Another version of copyFrom that loads associated data
Another version of copyFrom that loads associated data
I too stumbled upon this problem. This is the version we're using now. it's a bit better in that it doesn't unnecessarily delete the store or existing records if not needed. Might help someone who expects save() on a model to update associated records from server response too.
Code:// Override proxy reader so that it loads nested data from server response on model save() Ext.override(Ext.data.Model, { copyFrom: function (sourceRecord) { var result = this.callParent(arguments); var n = 0; var me = this; if (sourceRecord && sourceRecord.associations.length) { for (assocCount = sourceRecord.associations.length; n < assocCount; n++) { association = sourceRecord.associations.getAt(n); if (association.type === 'belongsTo') { break; } var storeName = association.storeName; if (typeof (me[storeName]) === 'undefined') { me[storeName] = Ext.clone(sourceRecord[storeName]); } // We don't want to replace the store if it already exists as it could have events bound already else { // The result could be without the store entirely since the server deemed nothing changed. // If the server want's to tell us that all items in the association have been removed it must send an empty array in json response. if (sourceRecord[storeName]) { var toAdd = [], toRemove = []; // Add new records, update existing records (again existing records may have event handlers bound so we can't just replace them) sourceRecord[storeName].each(function (record) { var meRecord = me[storeName].getById(record.get('id')); if (meRecord) { meRecord.copyFrom(record); } else { toAdd = record; } }, this); me[storeName].add(toAdd); // Remove removed records me[storeName].each(function (record) { if (sourceRecord[storeName].getById(record.get('id') === null)) { toRemove.push(record); } }, this); me[storeName].remove(toRemove); } } } } return result; }, });
You found a bug! We've classified it as
EXTJSIV-6710
.
We encourage you to continue the discussion and to find an acceptable workaround while we work on a permanent fix.


Reply With Quote