Last active
April 18, 2018 19:39
-
-
Save jratcliff/5434f2a20a874e6f4e2da155d00db2fc to your computer and use it in GitHub Desktop.
Ext.data.Model overrides for nested/keyless associations in 6.x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Overrides for Ext.data.Model | |
*/ | |
Ext.define('overrides.data.Model', { | |
override: 'Ext.data.Model', | |
privates: { | |
/** | |
* Override that adds support to check associations | |
*/ | |
isDirty: function (includeAssociations) { | |
var me = this, | |
associations = me.associations, | |
assocName, assoc, assocInstance, | |
isDirty = this.dirty, // default to the 'dirty' property on the record; | |
len, rec, i; | |
// check associations if asked but we only need to if the current record is not dirty | |
if (includeAssociations && isDirty !== true) { | |
for (assocName in associations) { | |
if (associations.hasOwnProperty(assocName)) { | |
if (isDirty) { | |
break; | |
} | |
assoc = associations[assocName]; | |
if (assoc.getterName) { | |
// call the getterName so that it will create the store/model if needed | |
assocInstance = me[assoc.getterName](); | |
if (assocInstance) { | |
if (assocInstance.isModel) { | |
isDirty = assocInstance.dirty; | |
} else { | |
len = assocInstance.getCount(); | |
for (i = 0; i < len; i++) { | |
rec = assocInstance.getAt(i); | |
isDirty = rec.dirty; | |
if (isDirty) { | |
break; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return isDirty; | |
} | |
}, | |
/** | |
* Override of the reject method that will also loop through and reject | |
* changes on all associations if the includeAssociations param is passed. | |
* JIRA: EXTJS-12027, EXTJS-16449 | |
* @param silent | |
* @param includeAssociations | |
*/ | |
reject: function (silent, includeAssociations) { | |
var me = this, | |
associations = me.associations, | |
assocName, assoc, assocInstance; | |
if (includeAssociations) { | |
for (assocName in associations) { | |
if (associations.hasOwnProperty(assocName)) { | |
assoc = associations[assocName]; | |
if (assoc.getterName) { | |
// call the getterName so that it will create the store/model if needed | |
assocInstance = me[assoc.getterName](); | |
if (assocInstance) { | |
if (assocInstance.isModel) { | |
assocInstance.reject(silent, includeAssociations); | |
} else { | |
assocInstance.rejectChanges(); | |
} | |
} | |
} | |
} | |
} | |
} | |
me.callParent(arguments); | |
}, | |
/** | |
* Override that will make sure associations are also updated. | |
* This means you can pass in a nested object to the set method | |
* and as long as this nested object matches what the server would | |
* normally send for associations, then the set method will update | |
* these associations. | |
* | |
* This override also means that any call to the Ext.data.Model#save method | |
* will now update associations on the client records if the server sends | |
* back any associations. | |
*/ | |
set: function (fieldName, newValue, options) { | |
var me = this, | |
single = Ext.isString(fieldName), | |
opt = (single ? options : newValue), | |
silent = opt && opt.silent, | |
commit = opt && opt.commit, | |
modifiedNames = me.callParent(arguments), | |
associations = me.associations, | |
assocName, assoc, assocInstance, values, | |
storeRecord, storeAssociations, storeAssocName, storeAssoc, storeCnt, i; | |
// Do not run this code if the model is loading data from its proxy. | |
// The framework already can handle loading associations that way. | |
// This code is for when a save to the server has been done and this | |
// set method is called to update the clientRecord with the serverRecord. | |
if (!me.isLoading() && modifiedNames) { | |
for (assocName in associations) { | |
if (associations.hasOwnProperty(assocName)) { | |
if (Ext.Array.contains(modifiedNames, assocName)) { | |
assoc = associations[assocName]; | |
if (single) { | |
values = me._singleProp; | |
values[fieldName] = newValue; | |
} else { | |
values = fieldName; | |
} | |
if (assoc.getterName) { | |
// call the getterName so that it will create the store/model if needed | |
assocInstance = me[assoc.getterName](); | |
if (assocInstance) { | |
if (assocInstance.isStore) { | |
assocInstance.suspendEvents(true); // make sure to queue the events | |
assocInstance.removeAll(true); | |
// call loadRawData() instead of add() so the data is processed by the reader | |
assocInstance.loadRawData(values[assocName]); | |
storeCnt = assocInstance.getCount(); | |
// after the data has been loaded we now will have proper records | |
// and for each record we need to setup any associations | |
for (i = 0; i < storeCnt; i++) { | |
storeRecord = assocInstance.getAt(i); | |
storeAssociations = storeRecord.associations; | |
for (storeAssocName in storeAssociations) { | |
if (storeAssociations.hasOwnProperty(storeAssocName)) { | |
storeAssoc = storeAssociations[storeAssocName]; | |
// call the setter if one is defined and send to it the associatedEntity | |
if (storeAssoc.setterName && storeRecord[storeAssoc.setterName] && assocInstance.associatedEntity) { | |
storeRecord[storeAssoc.setterName](assocInstance.associatedEntity); | |
} | |
} | |
} | |
} | |
if (commit) { | |
assocInstance.commitChanges(); | |
} | |
assocInstance.resumeEvents(); | |
delete me.data[assocName]; | |
} | |
else if (assocInstance.isModel) { | |
assocInstance.set(values[assocName]); | |
if (commit) { | |
assocInstance.commit(silent); | |
} | |
delete me.data[assocName]; | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
return modifiedNames; | |
}, | |
/** | |
* Custom version of the #getData method that only returns data for fields that are defined. | |
*/ | |
getStrictData: function (options) { | |
var me = this, | |
ret = {}, | |
opts = (options === true) ? me._getAssociatedOptions : (options || ret), // cheat | |
data = me.data, | |
associated = opts.associated, | |
changes = opts.changes, | |
critical = changes && opts.critical, | |
content = changes ? me.modified : data, | |
fieldsMap = me.fieldsMap, | |
persist = opts.persist, | |
serialize = opts.serialize, | |
criticalFields, field, n, name, value; | |
// DON'T use "opts" from here on... | |
// Keep in mind the two legacy use cases: | |
// - getData() ==> Ext.apply({}, me.data) | |
// - getData(true) ==> Ext.apply(Ext.apply({}, me.data), me.getAssociatedData()) | |
if (content) { // when processing only changes, me.modified could be null | |
for (name in content) { | |
if (content.hasOwnProperty(name)) { | |
value = data[name]; | |
field = fieldsMap[name]; | |
if (field) { | |
if (persist && !field.persist) { | |
continue; | |
} | |
if (serialize && field.serialize) { | |
value = field.serialize(value, me); | |
} | |
} | |
// for strict, only add if we have a field definition | |
if (field) { | |
ret[name] = value; | |
} | |
} | |
} | |
} | |
if (critical) { | |
criticalFields = me.self.criticalFields || me.getCriticalFields(); | |
for (n = criticalFields.length; n-- > 0;) { | |
name = (field = criticalFields[n]).name; | |
if (!(name in ret)) { | |
value = data[name]; | |
if (serialize && field.serialize) { | |
value = field.serialize(value, me); | |
} | |
ret[name] = value; | |
} | |
} | |
} | |
if (associated) { | |
me.getAssociatedData(ret, opts); // pass ret so new data is added to our object | |
} | |
return ret; | |
} | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment