Skip to content

Instantly share code, notes, and snippets.

@jratcliff
Last active April 18, 2018 19:39
Show Gist options
  • Save jratcliff/5434f2a20a874e6f4e2da155d00db2fc to your computer and use it in GitHub Desktop.
Save jratcliff/5434f2a20a874e6f4e2da155d00db2fc to your computer and use it in GitHub Desktop.
Ext.data.Model overrides for nested/keyless associations in 6.x
/**
* 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