/**
* ProxyStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.BufferedStore}.
* It's never used directly, but offers a set of methods used by both of those subclasses.
*
* We've left it here in the docs for reference purposes, but unless you need to make a whole new
* type of Store, what you're probably looking for is {@link Ext.data.Store}. If you're still
* interested, here's a brief description of what ProxyStore is and is not.
*
* ProxyStore provides the basic configuration for anything that can be considered a Store.
* It expects to be given a {@link Ext.data.Model Model} that represents the type of data
* in the Store. It also expects to be given a {@link Ext.data.proxy.Proxy Proxy} that handles
* the loading of data into the Store.
*
* ProxyStore provides a few helpful methods such as {@link #method-load} and {@link #sync},
* which load and save data respectively, passing the requests through the configured
* {@link #proxy}.
*
* Built-in Store subclasses add extra behavior to each of these functions. Note also that each
* ProxyStore subclass has its own way of storing data - in {@link Ext.data.Store} the data
* is saved as a flat {@link Ext.util.Collection Collection}, whereas in
* {@link Ext.data.BufferedStore BufferedStore} we use a {@link Ext.data.PageMap} to maintain
* a client side cache of pages of records.
*
* The store provides filtering and sorting support. This sorting/filtering can happen on the
* client side or can be completed on the server. This is controlled by the
* {@link Ext.data.Store#remoteSort remoteSort} and {@link Ext.data.Store#remoteFilter remoteFilter}
* config options. For more information see the {@link #method-sort} and
* {@link Ext.data.Store#filter filter} methods.
*/
Ext.define('Ext.data.ProxyStore', {
extend: 'Ext.data.AbstractStore',
requires: [
'Ext.data.Model',
'Ext.data.proxy.Proxy',
'Ext.data.proxy.Memory',
'Ext.data.operation.*'
],
config: {
// @cmd-auto-dependency {aliasPrefix: "model.", mvc: true, blame: "all"}
/**
* @cfg {String/Ext.data.Model} model
* Name of the {@link Ext.data.Model Model} associated with this store. See
* {@link Ext.data.Model#entityName}.
*
* May also be the actual Model subclass.
*
* This config is required for the store to be able to read data unless you have
* defined the {@link #fields} config which will create an anonymous
* `Ext.data.Model`.
*/
model: undefined,
// @cmd-auto-dependency {aliasPrefix: "data.field."}
/**
* @cfg fields
* @inheritdoc Ext.data.Model#cfg-fields
*
* @localdoc **Note:** In general, this configuration option should only be used
* for simple stores like a two-field store of
* {@link Ext.form.field.ComboBox ComboBox}. For anything more complicated, such
* as specifying a particular id property or associations, a
* {@link Ext.data.Model Model} should be defined and specified for the
* {@link #model} config.
*
* @since 2.3.0
*/
fields: null,
// @cmd-auto-dependency {aliasPrefix : "proxy."}
/**
* @cfg {String/Ext.data.proxy.Proxy/Object} proxy
* The Proxy to use for this Store. This can be either a string, a config object
* or a Proxy instance - see {@link #setProxy} for details.
* @since 1.1.0
*/
proxy: undefined,
/**
* @cfg {Boolean/Object} autoLoad
* If data is not specified, and if autoLoad is true or an Object, this store's
* load method is automatically called after creation. If the value of autoLoad
* is an Object, this Object will be passed to the store's load method.
*
* It's important to note that {@link Ext.data.TreeStore Tree Stores} will
* load regardless of autoLoad's value if expand is set to true on the
* {@link Ext.data.TreeStore#root root node}.
*
* @since 2.3.0
*/
autoLoad: undefined,
/**
* @cfg {Boolean} autoSync
* True to automatically sync the Store with its Proxy after every edit to one of
* its Records. Defaults to false.
*/
autoSync: false,
/**
* @cfg {String} batchUpdateMode
* Sets the updating behavior based on batch synchronization. 'operation' (the
* default) will update the Store's internal representation of the data after
* each operation of the batch has completed, 'complete' will wait until the
* entire batch has been completed before updating the Store's data. 'complete'
* is a good choice for local storage proxies, 'operation' is better for remote
* proxies, where there is a comparatively high latency.
*/
batchUpdateMode: 'operation',
/**
* @cfg {Boolean} sortOnLoad
* If true, any sorters attached to this Store will be run after loading data,
* before the datachanged event is fired. Defaults to true, ignored if
* {@link Ext.data.Store#remoteSort remoteSort} is true
*/
sortOnLoad: true,
/**
* @cfg {Boolean} trackRemoved
* This config controls whether removed records are remembered by this store for
* later saving to the server.
*/
trackRemoved: true,
/**
* @cfg {Boolean} asynchronousLoad
* This defaults to `true` when this store's {@link #cfg-proxy} is asynchronous,
* such as an {@link Ext.data.proxy.Ajax Ajax proxy}.
*
* When the proxy is synchronous, such as a {@link Ext.data.proxy.Memory} memory
* proxy, this defaults to `false`.
*
* *NOTE:* This does not cause synchronous Ajax requests if configured `false`
* when an Ajax proxy is used. It causes immediate issuing of an Ajax request
* when {@link #method-load} is called rather than issuing the request at the end
* of the current event handler run.
*
* What this means is that when using an Ajax proxy, calls to
* {@link #method-load} do not fire the request to the remote resource
* immediately, but schedule a request to be made. This is so that multiple
* requests are not fired when mutating a store's remote filters and sorters (as
* happens during state restoration). The request is made only once after all
* relevant store state is fully set.
*
* @since 6.0.1
*/
asynchronousLoad: undefined
},
onClassExtended: function(cls, data, hooks) {
var model = data.model,
onBeforeClassCreated;
if (typeof model === 'string') {
onBeforeClassCreated = hooks.onBeforeCreated;
hooks.onBeforeCreated = function() {
var me = this,
args = arguments;
Ext.require(model, function() {
onBeforeClassCreated.apply(me, args);
});
};
}
},
/**
* @private
* @property {Boolean} implicitModel
* The class name of the model that this store uses if no explicit {@link #model} is
* given
*/
implicitModel: 'Ext.data.Model',
/**
* @property {Object} lastOptions
* Property to hold the last options from a {@link #method-load} method call. This
* object is used for the {@link #method-reload} to reuse the same options. Please
* see {@link #method-reload} for a simple example on how to use the lastOptions
* property.
*/
/**
* @property {Number} autoSyncSuspended
* A counter to track suspensions.
* @private
*/
autoSyncSuspended: 0,
/**
* @property {Ext.data.Model[]} removed
* Temporary cache in which removed model instances are kept until successfully
* synchronised with a Proxy, at which point this is cleared.
*
* This cache is maintained unless you set `trackRemoved` to `false`.
*
* @protected
* @readonly
*/
removed: null,
/**
* @event beforeload
* Fires before a request is made for a new data object. If the beforeload handler returns
* `false` the load action will be canceled.
*
* **Note:** If you are using a buffered store, you should use
* {@link Ext.data.Store#beforeprefetch beforeprefetch}.
* @param {Ext.data.Store} store This Store
* @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object
* that will be passed to the Proxy to load the Store
* @since 1.1.0
*/
/**
* @event load
* Fires whenever the store reads data from a remote data source.
*
* **Note:** If you are using a buffered store, you should use
* {@link Ext.data.Store#prefetch prefetch}.
* @param {Ext.data.Store} this
* @param {Ext.data.Model[]} records An array of records
* @param {Boolean} successful True if the operation was successful.
* @param {Ext.data.operation.Read} operation The
* {@link Ext.data.operation.Read Operation} object that was used in the data
* load call
* @since 1.1.0
*/
/**
* @event write
* Fires whenever a successful write has been made via the configured {@link #proxy Proxy}
* @param {Ext.data.Store} store This Store
* @param {Ext.data.operation.Operation} operation The
* {@link Ext.data.operation.Operation Operation} object that was used in the write
* @since 3.4.0
*/
/**
* @event beforesync
* Fired before a call to {@link #sync} is executed. Return false from any listener to cancel
* the sync
* @param {Object} options Hash of all records to be synchronized, broken down into create,
* update and destroy
*/
/**
* @event metachange
* Fires when this store's underlying reader (available via the proxy) provides new metadata.
* Metadata usually consists of new field definitions, but can include any configuration data
* required by an application, and can be processed as needed in the event handler.
* This event is currently only fired for JsonReaders.
* @param {Ext.data.Store} this
* @param {Object} meta The JSON metadata
* @since 1.1.0
*/
constructor: function(config) {
var me = this;
//<debug>
var configModel = me.model; // eslint-disable-line vars-on-top, one-var
//</debug>
me.callParent(arguments);
if (me.getAsynchronousLoad() === false) {
me.flushLoad();
}
//<debug>
if (!me.getModel() && me.useModelWarning !== false &&
me.getStoreId() !== 'ext-empty-store') {
// There are a number of ways things could have gone wrong, try to give as much
// information as possible
var logMsg = [ // eslint-disable-line vars-on-top, one-var
Ext.getClassName(me) || 'Store',
' created with no model.'
];
if (typeof configModel === 'string') {
logMsg.push(" The name '", configModel, "'",
' does not correspond to a valid model.');
}
Ext.log.warn(logMsg.join(''));
}
//</debug>
},
/**
* @private
*/
doDestroy: function() {
var me = this,
proxy = me.getProxy();
me.clearLoadTask();
Ext.destroy(me.getData());
me.data = null;
me.setProxy(null);
if (proxy.autoCreated) {
proxy.destroy();
}
me.setModel(null);
me.callParent();
},
applyAsynchronousLoad: function(asynchronousLoad) {
// Default in an asynchronousLoad setting.
// It defaults to false if the proxy is synchronous, and true if the proxy is asynchronous.
if (asynchronousLoad == null) {
asynchronousLoad = !this.loadsSynchronously();
}
return asynchronousLoad;
},
updateAutoLoad: function(autoLoad) {
// Ensure the data collection is set up
this.getData();
if (autoLoad) {
// Defer the load until idle, when the store (and probably the view)
// is fully constructed
this.load(Ext.isObject(autoLoad) ? autoLoad : undefined);
}
},
/**
* Returns the total number of {@link Ext.data.Model Model} instances that the
* {@link Ext.data.proxy.Proxy Proxy} indicates exist. This will usually differ from
* {@link #getCount} when using paging - getCount returns the number of records loaded into
* the Store at the moment, getTotalCount returns the number of records that could be loaded
* into the Store if the Store contained all data
* @return {Number} The total number of Model instances available via the Proxy. 0 returned if
* no value has been set via the reader.
*/
getTotalCount: function() {
return this.totalCount || 0;
},
applyFields: function(fields) {
if (fields) {
this.createImplicitModel(fields);
}
},
applyModel: function(model) {
if (model) {
model = Ext.data.schema.Schema.lookupEntity(model);
}
else if (!this.destroying) {
// If no model, ensure that the fields config is converted to a model.
this.getFields();
model = this.getModel() || this.createImplicitModel();
}
return model;
},
applyProxy: function(proxy) {
var model = this.getModel();
if (proxy !== null) {
if (proxy) {
if (proxy.isProxy) {
proxy.setModel(model);
}
else {
if (Ext.isString(proxy)) {
proxy = {
type: proxy,
model: model
};
}
else if (!proxy.model) {
proxy = Ext.apply({
model: model
}, proxy);
}
proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
proxy.autoCreated = true;
}
}
else if (model) {
proxy = model.getProxy();
this.useModelProxy = true;
}
if (!proxy) {
proxy = Ext.createByAlias('proxy.memory');
proxy.autoCreated = true;
}
}
return proxy;
},
applyState: function(state) {
var me = this;
me.callParent([state]);
// This is called during construction. Sorters and filters might have changed
// which require a reload.
// If autoLoad is true, it might have loaded synchronously from a memory proxy,
// so needs to reload.
// If it is already loaded, we definitely need to reload to apply the state.
// If we're not remote sorting or filtering, there is no point in loading.
if ((me.getAutoLoad() || me.isLoaded()) &&
(me.getRemoteSort() || me.getRemoteFilter())) {
me.load();
}
},
updateProxy: function(proxy, oldProxy) {
this.proxyListeners = Ext.destroy(this.proxyListeners);
},
updateTrackRemoved: function(track) {
this.cleanRemoved();
this.removed = track ? [] : null;
},
/**
* @private
*/
onMetaChange: function(proxy, meta) {
this.fireEvent('metachange', this, meta);
},
// saves any phantom records
create: function(data, options) {
var me = this,
Model = me.getModel(),
instance = new Model(data),
operation;
options = Ext.apply({}, options);
if (!options.records) {
options.records = [instance];
}
options.internalScope = me;
options.internalCallback = me.onProxyWrite;
operation = me.createOperation('create', options);
return operation.execute();
},
read: function() {
return this.load.apply(this, arguments);
},
update: function(options) {
var me = this,
operation;
options = Ext.apply({}, options);
if (!options.records) {
options.records = me.getUpdatedRecords();
}
options.internalScope = me;
options.internalCallback = me.onProxyWrite;
operation = me.createOperation('update', options);
return operation.execute();
},
/**
* @private
* Callback for any write Operation over the Proxy. Updates the Store's MixedCollection
* to reflect the updates provided by the Proxy
*/
onProxyWrite: function(operation) {
var me = this,
success = operation.wasSuccessful(),
records = operation.getRecords();
switch (operation.getAction()) {
case 'create':
me.onCreateRecords(records, operation, success);
break;
case 'update':
me.onUpdateRecords(records, operation, success);
break;
case 'destroy':
me.onDestroyRecords(records, operation, success);
break;
}
if (success) {
me.fireEvent('write', me, operation);
me.fireEvent('datachanged', me);
}
},
// may be implemented by store subclasses
onCreateRecords: Ext.emptyFn,
// may be implemented by store subclasses
onUpdateRecords: Ext.emptyFn,
/**
* Removes any records when a write is returned from the server.
* @private
* @param {Ext.data.Model[]} records The array of removed records
* @param {Ext.data.operation.Operation} operation The operation that just completed
* @param {Boolean} success True if the operation was successful
*/
onDestroyRecords: function(records, operation, success) {
if (success) {
this.cleanRemoved();
}
},
// tells the attached proxy to destroy the given records
// @since 3.4.0
erase: function(options) {
var me = this,
operation;
options = Ext.apply({}, options);
if (!options.records) {
options.records = me.getRemovedRecords();
}
options.internalScope = me;
options.internalCallback = me.onProxyWrite;
operation = me.createOperation('destroy', options);
return operation.execute();
},
/**
* @private
* Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default
* just calls through to onProxyWrite.
*/
onBatchOperationComplete: function(batch, operation) {
return this.onProxyWrite(operation);
},
/**
* @private
* Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch
* operations and updates the Store's internal data MixedCollection.
*/
onBatchComplete: function(batch, operation) {
var me = this,
operations = batch.operations,
length = operations.length,
i;
if (me.batchUpdateMode !== 'operation') {
me.suspendEvents();
for (i = 0; i < length; i++) {
me.onProxyWrite(operations[i]);
}
me.resumeEvents();
}
me.isSyncing = false;
if (batch.$destroyOwner === me) {
batch.destroy();
}
me.fireEvent('datachanged', me);
},
/**
* @private
*/
onBatchException: function(batch, operation) {
// //decide what to do... could continue with the next operation
// batch.start();
//
// //or retry the last operation
// batch.retry();
},
/**
* @private
* Filter function for new records.
*/
filterNew: function(item) {
// only want phantom records that are valid
return item.phantom && item.isValid();
},
/**
* Returns all `{@link Ext.data.Model#property-phantom phantom}` records in this store.
* @return {Ext.data.Model[]} A possibly empty array of `phantom` records.
*/
getNewRecords: function() {
return [];
},
/**
* Returns all valid, non-phantom Model instances that have been updated in the Store but
* not yet synchronized with the Proxy.
* @return {Ext.data.Model[]} The updated Model instances
*/
getUpdatedRecords: function() {
return [];
},
/**
* Gets all {@link Ext.data.Model records} added or updated since the last commit. Note that
* the order of records returned is not deterministic and does not indicate the order in which
* records were modified. Note also that removed records are not included
* (use {@link #getRemovedRecords} for that).
* @return {Ext.data.Model[]} The added and updated Model instances
*/
getModifiedRecords: function() {
return [].concat(this.getNewRecords(), this.getUpdatedRecords());
},
/**
* @private
* Filter function for updated records.
*/
filterUpdated: function(item) {
// only want dirty records, not phantoms that are valid
return item.dirty && !item.phantom && item.isValid();
},
/**
* Returns any records that have been removed from the store but not yet destroyed on the proxy.
* @return {Ext.data.Model[]} The removed Model instances. Note that this is a *copy* of the
* store's array, so may be mutated.
*/
getRemovedRecords: function() {
var removed = this.getRawRemovedRecords();
return removed ? Ext.Array.clone(removed) : [];
},
/**
* Synchronizes the store with its {@link #proxy}. This asks the proxy to batch together any
* new, updated and deleted records in the store, updating the store's internal representation
* of the records as each operation completes.
*
* @param {Object} [options] Object containing one or more properties supported by the sync
* method (these get passed along to the underlying proxy's {@link Ext.data.Proxy#batch batch}
* method):
*
* @param {Ext.data.Batch/Object} [options.batch] A {@link Ext.data.Batch} object (or batch
* config to apply to the created batch). If unspecified a default batch will be auto-created
* as needed.
*
* @param {Function} [options.callback] The function to be called upon completion of the sync.
* The callback is called regardless of success or failure and is passed the following
* parameters:
* @param {Ext.data.Batch} options.callback.batch The {@link Ext.data.Batch batch} that was
* processed, containing all operations in their current state after processing
* @param {Object} options.callback.options The options argument that was originally passed
* into sync
*
* @param {Function} [options.success] The function to be called upon successful completion
* of the sync. The success function is called only if no exceptions were reported in any
* operations. If one or more exceptions occurred then the failure function will be called
* instead. The success function is called with the following parameters:
* @param {Ext.data.Batch} options.success.batch The {@link Ext.data.Batch batch} that was
* processed, containing all operations in their current state after processing
* @param {Object} options.success.options The options argument that was originally passed
* into sync
*
* @param {Function} [options.failure] The function to be called upon unsuccessful completion
* of the sync. The failure function is called when one or more operations returns an exception
* during processing (even if some operations were also successful). In this case you can check
* the batch's {@link Ext.data.Batch#exceptions exceptions} array to see exactly which
* operations had exceptions. The failure function is called with the following parameters:
* @param {Ext.data.Batch} options.failure.batch The {@link Ext.data.Batch} that was processed,
* containing all operations in their current state after processing
* @param {Object} options.failure.options The options argument that was originally passed
* into sync
*
* @param {Object} [options.params] Additional params to send during the sync Operation(s).
*
* @param {Object} [options.scope] The scope in which to execute any callbacks (i.e. the `this`
* object inside the callback, success and/or failure functions). Defaults to the store's proxy.
*
* @return {Ext.data.Store} this
*/
sync: function(options) {
var me = this,
operations = {},
toCreate = me.getNewRecords(),
toUpdate = me.getUpdatedRecords(),
toDestroy = me.getRemovedRecords(),
needsSync = false;
//<debug>
if (me.isSyncing) {
Ext.log.warn('Sync called while a sync operation is in progress. ' +
'Consider configuring autoSync as false.');
}
//</debug>
me.needsSync = false;
if (toCreate.length > 0) {
operations.create = toCreate;
needsSync = true;
}
if (toUpdate.length > 0) {
operations.update = toUpdate;
needsSync = true;
}
if (toDestroy.length > 0) {
operations.destroy = toDestroy;
needsSync = true;
}
if (needsSync && me.fireEvent('beforesync', operations) !== false) {
me.isSyncing = true;
options = options || {};
me.proxy.batch(Ext.apply(options, {
operations: operations,
listeners: me.getBatchListeners(),
$destroyOwner: me
}));
}
return me;
},
/**
* @private
* Returns an object which is passed in as the listeners argument to proxy.batch inside
* this.sync. This is broken out into a separate function to allow for customisation
* of the listeners
* @return {Object} The listeners object
*/
getBatchListeners: function() {
var me = this,
listeners = {
scope: me,
exception: me.onBatchException,
complete: me.onBatchComplete
};
if (me.batchUpdateMode === 'operation') {
listeners.operationcomplete = me.onBatchOperationComplete;
}
return listeners;
},
/**
* Saves all pending changes via the configured {@link #proxy}. Use {@link #sync} instead.
* @deprecated 4.0.0 Will be removed in the next major version
*/
save: function() {
return this.sync.apply(this, arguments);
},
/**
* Marks this store as needing a load. When the current executing event handler exits,
* this store will send a request to load using its configured {@link #proxy}.
*
* Upon return of the data from whatever data source the proxy connected to, the retrieved
* {@link Ext.data.Model records} will be loaded into this store, and the optional callback
* will be called. Example usage:
*
* store.load({
* scope: this,
* callback: function(records, operation, success) {
* // the operation object
* // contains all of the details of the load operation
* console.log(records);
* }
* });
*
* If the callback scope does not need to be set, a function can simply be passed:
*
* store.load(function(records, operation, success) {
* console.log('loaded records');
* });
*
* @param {Object} [options] This is passed into the
* {@link Ext.data.operation.Operation Operation} object that is created and then sent to the
* proxy's {@link Ext.data.proxy.Proxy#read} function. In addition to the options listed below,
* this object may contain properties to configure the
* {@link Ext.data.operation.Operation Operation}.
* @param {Function} [options.callback] A function which is called when the response arrives.
* @param {Ext.data.Model[]} options.callback.records Array of records.
* @param {Ext.data.operation.Operation} options.callback.operation The Operation itself.
* @param {Boolean} options.callback.success `true` when operation completed successfully.
* @param {Boolean} [options.addRecords=false] Specify as `true` to *add* the incoming records
* rather than the default which is to have the incoming records *replace* the existing store
* contents.
*
* @return {Ext.data.Store} this
* @since 1.1.0
*/
load: function(options) {
var me = this;
// Legacy option. Specifying a function was allowed.
if (typeof options === 'function') {
options = {
callback: options
};
}
else {
// We may mutate the options object in setLoadOptions.
options = options ? Ext.Object.chain(options) : {};
}
me.pendingLoadOptions = options;
// If we are configured to load asynchronously (the default for async proxies)
// then schedule a flush, unless one is already scheduled.
if (me.getAsynchronousLoad()) {
if (!me.loadTimer) {
me.loadTimer = Ext.asap(me.flushLoad, me);
}
}
// If we are configured to load synchronously (the default for sync proxies)
// then flush the load now.
else {
me.flushLoad();
}
return me;
},
/**
* Called when the event handler which called the {@link #method-load} method exits.
*/
flushLoad: function() {
var me = this,
options = me.pendingLoadOptions,
operation;
if (me.destroying || me.destroyed) {
return;
}
// If it gets called programatically before the timer fired, the listener will need
// cancelling.
me.clearLoadTask();
if (!options) {
return;
}
me.setLoadOptions(options);
if (me.getRemoteSort() && options.sorters) {
me.fireEvent('beforesort', me, options.sorters);
}
operation = Ext.apply({
internalScope: me,
internalCallback: me.onProxyLoad,
scope: me
}, options);
me.lastOptions = operation;
operation = me.createOperation('read', operation);
if (me.fireEvent('beforeload', me, operation) !== false) {
me.onBeforeLoad(operation);
me.loading = true;
// Internal event, fired after the flag is set, we need
// to fire this beforeload is too early
if (me.hasListeners.beginload) {
me.fireEvent('beginload', me, operation);
}
operation.execute();
}
else {
if (me.getAsynchronousLoad()) {
operation.abort();
}
operation.setCompleted();
}
},
/**
* Reloads the store using the last options passed to the {@link #method-load} method.
* You can use the reload method to reload the store using the parameters from the last load()
* call. For example:
*
* store.load({
* params : {
* userid : 22216
* }
* });
*
* //...
*
* store.reload();
*
* The initial {@link #method-load} execution will pass the `userid` parameter in the request.
* The {@link #reload} execution will also send the same `userid` parameter in its request
* as it will reuse the `params` object from the last {@link #method-load} call.
*
* You can override a param by passing in the config object with the `params` object:
*
* store.load({
* params : {
* userid : 22216,
* foo : 'bar'
* }
* });
*
* //...
*
* store.reload({
* params : {
* userid : 1234
* }
* });
*
* The initial {@link #method-load} execution sends the `userid` and `foo` parameters but in the
* {@link #reload} it only sends the `userid` paramter because you are overriding the `params`
* config not just overriding the one param. To only change a single param but keep other
* params, you will have to get the last params from the {@link #lastOptions} property:
*
* // make a copy of the last params so we don't affect future reload() calls
* var lastOptions = store.lastOptions,
* lastParams = Ext.clone(lastOptions.params);
*
* lastParams.userid = 1234;
*
* store.reload({
* params : lastParams
* });
*
* This will now send the `userid` parameter as `1234` and the `foo` param as `'bar'`.
*
* @param {Object} [options] A config object which contains options which may override the
* options passed to the previous load call. See the
* {@link #method-load} method for valid configs.
*/
reload: function(options) {
return this.load(Ext.apply({}, options, this.lastOptions));
},
onEndUpdate: function() {
var me = this;
if (me.needsSync && me.autoSync && !me.autoSyncSuspended) {
me.sync();
}
},
/**
* @private
* A model instance should call this method on the Store it has been
* {@link Ext.data.Model#join joined} to.
* @param {Ext.data.Model} record The model instance that was edited
* @since 3.4.0
*/
afterReject: function(record) {
var me = this;
// Must pass the 5th param (modifiedFieldNames) as null, otherwise the
// event firing machinery appends the listeners "options" object to the arg list
// which may get used as the modified fields array by a handler.
// This array is used for selective grid cell updating by Grid View.
// Null will be treated as though all cells need updating.
if (me.contains(record)) {
me.onUpdate(record, Ext.data.Model.REJECT, null);
me.fireEvent('update', me, record, Ext.data.Model.REJECT, null);
me.fireEvent('datachanged', me);
}
},
/**
* A model instance should call this method on the Store it has been
* {@link Ext.data.Model#join joined} to.
* @param {Ext.data.Model} record The model instance that was edited.
* @param {String[]} [modifiedFieldNames] (private)
* @since 3.4.0
* @private
*/
afterCommit: function(record, modifiedFieldNames) {
var me = this;
if (!modifiedFieldNames) {
modifiedFieldNames = null;
}
if (me.contains(record)) {
me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames);
me.fireEvent('datachanged', me);
}
},
afterErase: function(record) {
this.onErase(record);
},
onErase: Ext.emptyFn,
onUpdate: Ext.emptyFn,
/**
* Returns true if the store has a pending load task.
* @return {Boolean} `true` if the store has a pending load task.
* @private
*/
hasPendingLoad: function() {
return !!this.pendingLoadOptions || this.isLoading();
},
/**
* Returns true if the Store is currently performing a load operation
* @return {Boolean} `true` if the Store is currently loading
*/
isLoading: function() {
return !!this.loading;
},
/**
* Returns `true` if the Store has been loaded.
* @return {Boolean} `true` if the Store has been loaded.
*/
isLoaded: function() {
return this.loadCount > 0;
},
/**
* Suspends automatically syncing the Store with its Proxy. Only applicable if
* {@link #autoSync} is `true`
*/
suspendAutoSync: function() {
++this.autoSyncSuspended;
},
/**
* Resumes automatically syncing the Store with its Proxy. Only applicable if
* {@link #autoSync} is `true`
* @param {Boolean} syncNow Pass `true` to synchronize now. Only synchronizes with the Proxy
* if the suspension count has gone to zero (We are not under a higher level of suspension)
*
*/
resumeAutoSync: function(syncNow) {
var me = this;
//<debug>
if (!me.autoSyncSuspended) {
Ext.log.warn('Mismatched call to resumeAutoSync - auto synchronization ' +
'is currently not suspended.');
}
//</debug>
if (me.autoSyncSuspended && ! --me.autoSyncSuspended) {
if (syncNow) {
me.sync();
}
}
},
/**
* Removes all records from the store. This method does a "fast remove",
* individual remove events are not called. The {@link #clear} event is
* fired upon completion.
* @method
* @since 1.1.0
*/
removeAll: Ext.emptyFn,
// individual store subclasses should implement a "fast" remove
// and fire a clear event afterwards
// to be implemented by subclasses
clearData: Ext.emptyFn,
privates: {
/**
* @private
* Returns the array of records which have been removed since the last time this store
* was synced.
*
* This is used internally, when purging removed records after a successful sync.
* This is overridden by TreeStore because TreeStore accumulates deleted records on removal
* of child nodes from their parent, *not* on removal of records from its collection.
* The collection has records added on expand, and removed on collapse.
*/
getRawRemovedRecords: function() {
return this.removed;
},
onExtraParamsChanged: function() {
},
clearLoadTask: function() {
this.pendingLoadOptions = this.loadTimer = Ext.unasap(this.loadTimer);
},
cleanRemoved: function() {
// Must use class-specific getRawRemovedRecords.
// Regular Stores add to the "removed" property on remove.
// TreeStores are having records removed all the time; node collapse removes.
// TreeStores add to the "removedNodes" property onNodeRemove
var removed = this.getRawRemovedRecords(),
len, i;
if (removed) {
for (i = 0, len = removed.length; i < len; ++i) {
removed[i].unjoin(this);
}
removed.length = 0;
}
},
createOperation: function(type, options) {
var me = this,
proxy = me.getProxy(),
listeners;
if (!me.proxyListeners) {
listeners = {
scope: me,
destroyable: true,
beginprocessresponse: me.beginUpdate,
endprocessresponse: me.endUpdate
};
if (!me.disableMetaChangeEvent) {
listeners.metachange = me.onMetaChange;
}
me.proxyListeners = proxy.on(listeners);
}
return proxy.createOperation(type, options);
},
createImplicitModel: function(fields) {
var me = this,
modelCfg = {
extend: me.implicitModel,
statics: {
defaultProxy: 'memory'
}
},
proxy, model;
if (fields) {
modelCfg.fields = fields;
}
model = Ext.define(null, modelCfg);
me.setModel(model);
proxy = me.getProxy();
if (proxy) {
model.setProxy(proxy);
}
else {
me.setProxy(model.getProxy());
}
},
loadsSynchronously: function() {
return this.getProxy().isSynchronous;
},
onBeforeLoad: Ext.privateFn,
removeFromRemoved: function(record) {
// Must use class-specific getRawRemovedRecords.
// Regular Stores add to the "removed" property on remove.
// TreeStores are having records removed all the time; node collapse removes.
// TreeStores add to the "removedNodes" property onNodeRemove
var removed = this.getRawRemovedRecords();
if (removed) {
Ext.Array.remove(removed, record);
record.unjoin(this);
}
},
setLoadOptions: function(options) {
var me = this,
filters, sorters;
if (me.getRemoteFilter()) {
filters = me.getFilters(false);
if (filters && filters.getCount()) {
options.filters = filters.getRange();
}
}
if (me.getRemoteSort()) {
sorters = me.getSorters(false);
if (sorters && sorters.getCount()) {
options.sorters = sorters.getRange();
}
}
}
}
});