/**
* A chained store is a store that is a "view" of an existing store. The data comes from the
* {@link #source}, however this view of the store may be sorted & filtered independently without
* having any impact on the {@link #source} store.
*/
Ext.define('Ext.data.ChainedStore', {
extend: 'Ext.data.AbstractStore',
alias: 'store.chained',
mixins: [
'Ext.data.LocalStore'
],
/**
* @property {Boolean} isChainedStore
* `true` in this class to identify an object as an instantiated ChainedStore, or subclass
* thereof.
*/
isChainedStore: true,
config: {
/**
* @cfg {Ext.data.Store/String} source
* The backing data source for this chained store. Either a store instance
* or the id of an existing store.
*/
source: null,
remoteFilter: false,
remoteSort: false
},
syncSourceGrouping: false,
//<debug>
updateRemoteFilter: function(remoteFilter, oldRemoteFilter) {
if (remoteFilter) {
Ext.raise('Remote filtering cannot be used with chained stores.');
}
this.callParent([remoteFilter, oldRemoteFilter]);
},
updateRemoteSort: function(remoteSort, oldRemoteSort) {
if (remoteSort) {
Ext.raise('Remote sorting cannot be used with chained stores.');
}
this.callParent([remoteSort, oldRemoteSort]);
},
//</debug>
remove: function() {
var source = this.getSource();
//<debug>
if (!source) {
Ext.raise('Cannot remove records with no source.');
}
//</debug>
return source.remove.apply(source, arguments);
},
removeAll: function() {
var source = this.getSource();
//<debug>
if (!source) {
Ext.raise('Cannot remove records with no source.');
}
//</debug>
return source.removeAll();
},
getData: function() {
var me = this,
data = me.data;
if (!data) {
me.data = data = me.constructDataCollection();
}
return data;
},
getTotalCount: function() {
return this.getCount();
},
getSession: function() {
return this.getSourceValue('getSession', null);
},
applySource: function(source) {
if (source) {
//<debug>
/* eslint-disable-next-line vars-on-top */
var original = source,
s;
//</debug>
source = Ext.data.StoreManager.lookup(source);
//<debug>
if (!source) {
s = 'Invalid source {0}specified for Ext.data.ChainedStore';
s = Ext.String.format(s, typeof original === 'string' ? '"' + original + '" ' : '');
Ext.raise(s);
}
//</debug>
}
return source;
},
updateSource: function(source, oldSource) {
var me = this,
data;
if (oldSource && !oldSource.destroyed) {
oldSource.removeObserver(me);
}
if (source) {
data = me.getData();
data.setSource(source.getData());
if (me.syncSourceGrouping) {
me.setGrouper(source.getGrouper());
}
if (!me.isInitializing) {
me.fireEvent('refresh', me);
me.fireEvent('datachanged', me);
}
source.addObserver(me);
}
},
/**
* Get the model used for this store.
* @return {Ext.data.Model} The model
*/
getModel: function() {
return this.getSourceValue('getModel', null);
},
getProxy: function() {
return null;
},
onCollectionAdd: function(collection, info) {
var me = this,
records = info.items,
lastChunk = !info.next;
// Collection add changes the items reference of the collection, and that array
// object if directly referenced by Ranges. The ranges have to refresh themselves
// upon add.
if (me.activeRanges) {
me.syncActiveRanges();
}
if (me.ignoreCollectionAdd) {
return;
}
me.fireEvent('add', me, records, info.at);
// If there is a next property, that means there is another range that needs
// to be removed after this. Wait until everything is gone before firign datachanged
// since it should be a bulk operation
if (lastChunk) {
me.fireEvent('datachanged', me);
}
},
// Our collection tells us that an item has changed
onCollectionItemChange: function(collection, info) {
var me = this,
record = info.item,
modifiedFieldNames = info.modified || null,
type = info.meta;
if (me.activeRanges && info.newIndex !== info.oldIndex) {
me.syncActiveRanges();
}
// Inform any interested parties that a record has been mutated.
// This will be invoked on TreeStores in which the invoking record
// is an descendant of a collapsed node, and so *will not be contained by this store
me.onUpdate(record, type, modifiedFieldNames, info);
me.fireEvent('update', me, record, type, modifiedFieldNames, info);
me.fireEvent('datachanged', me);
},
onCollectionUpdateKey: function(source, details) {
// Must react to upstream Collection key update by firing idchanged event
this.fireEvent('idchanged', this, details.item, details.oldKey, details.newKey);
},
onUpdate: Ext.emptyFn,
lastCollectionRefesh: null,
onCollectionRefresh: function(collection) {
var me = this,
gen = collection.generation;
if (!me.isConfiguring && me.lastCollectionRefesh !== gen) {
me.lastCollectionRefesh = gen;
me.fireEvent('datachanged', me);
me.fireEvent('refresh', me);
}
},
onCollectionRemove: function(collection, info) {
var me = this,
records = info.items,
lastChunk = !info.next;
if (me.ignoreCollectionRemove) {
return;
}
me.fireEvent('remove', me, records, info.at, false);
// If there is a next property, that means there is another range that needs
// to be removed after this. Wait until everything is gone before firign datachanged
// since it should be a bulk operation
if (lastChunk) {
me.fireEvent('datachanged', me);
}
},
onSourceBeforeLoad: function(source, operation) {
this.fireEvent('beforeload', this, operation);
this.callObservers('BeforeLoad', [operation]);
},
onSourceAfterLoad: function(source, records, successful, operation) {
this.fireEvent('load', this, records, successful, operation);
this.callObservers('AfterLoad', [records, successful, operation]);
},
onFilterEndUpdate: function() {
var me = this;
if (me.getData().generation === me.lastCollectionRefesh && !me.getRemoteFilter()) {
me.suppressNextFilter = true;
}
me.callParent(arguments);
me.callObservers('Filter');
},
onSourceBeforePopulate: function() {
this.ignoreCollectionAdd = true;
this.callObservers('BeforePopulate');
},
onSourceAfterPopulate: function() {
var me = this;
me.ignoreCollectionAdd = false;
me.fireEvent('datachanged', me);
me.fireEvent('refresh', me);
this.callObservers('AfterPopulate');
},
onSourceBeforeClear: function() {
this.ignoreCollectionRemove = true;
this.callObservers('BeforeClear');
},
onSourceAfterClear: function() {
this.ignoreCollectionRemove = false;
this.callObservers('AfterClear');
},
onSourceBeforeRemoveAll: function() {
this.ignoreCollectionRemove = true;
this.callObservers('BeforeRemoveAll');
},
onSourceAfterRemoveAll: function(source, silent) {
var me = this;
me.ignoreCollectionRemove = false;
if (!silent) {
me.fireEvent('clear', me);
me.fireEvent('datachanged', me);
}
this.callObservers('AfterRemoveAll', [silent]);
},
onSourceFilter: function() {
var me = this,
gen = me.getData().generation;
if (me.lastCollectionRefesh !== gen) {
me.lastCollectionRefesh = gen;
me.fireEvent('refresh', me);
me.fireEvent('datachanged', me);
}
},
onSourceGrouperChange: function(source, grouper) {
if (this.syncSourceGrouping) {
this.setGrouper(grouper);
}
},
hasPendingLoad: function() {
return this.getSourceValue('hasPendingLoad', false);
},
isLoaded: function() {
return this.getSourceValue('isLoaded', false);
},
isLoading: function() {
return this.getSourceValue('isLoading', false);
},
doDestroy: function() {
var me = this;
me.observers = null;
me.setSource(null);
me.getData().destroy(true);
me.data = null;
me.callParent();
},
privates: {
getSourceValue: function(method, defaultValue) {
var source = this.getSource(),
val = defaultValue;
if (source && !source.destroyed) {
val = source[method]();
}
return val;
},
isMoving: function() {
var source = this.getSource();
return source.isMoving ? source.isMoving.apply(source, arguments) : false;
},
loadsSynchronously: function() {
return this.getSource().loadsSynchronously();
}
}
// Provides docs from the mixin
/**
* @method add
* @inheritdoc Ext.data.LocalStore#add
*/
/**
* @method each
* @inheritdoc Ext.data.LocalStore#each
*/
/**
* @method collect
* @inheritdoc Ext.data.LocalStore#collect
*/
/**
* @method getById
* @inheritdoc Ext.data.LocalStore#getById
*/
/**
* @method getByInternalId
* @inheritdoc Ext.data.LocalStore#getByInternalId
*/
/**
* @method indexOf
* @inheritdoc Ext.data.LocalStore#indexOf
*/
/**
* @method indexOfId
* @inheritdoc Ext.data.LocalStore#indexOfId
*/
/**
* @method insert
* @inheritdoc Ext.data.LocalStore#insert
*/
/**
* @method queryBy
* @inheritdoc Ext.data.LocalStore#queryBy
*/
/**
* @method query
* @inheritdoc Ext.data.LocalStore#query
*/
/**
* @method first
* @inheritdoc Ext.data.LocalStore#first
*/
/**
* @method last
* @inheritdoc Ext.data.LocalStore#last
*/
/**
* @method sum
* @inheritdoc Ext.data.LocalStore#sum
*/
/**
* @method count
* @inheritdoc Ext.data.LocalStore#count
*/
/**
* @method min
* @inheritdoc Ext.data.LocalStore#min
*/
/**
* @method max
* @inheritdoc Ext.data.LocalStore#max
*/
/**
* @method average
* @inheritdoc Ext.data.LocalStore#average
*/
/**
* @method aggregate
* @inheritdoc Ext.data.LocalStore#aggregate
*/
});