Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / AbstractStore.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-data-AbstractStore'>/**
19 </span> * @author Ed Spencer
20  *
21  * AbstractStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.TreeStore}. It's never used directly,
22  * but offers a set of methods used by both of those subclasses.
23  * 
24  * We've left it here in the docs for reference purposes, but unless you need to make a whole new type of Store, what
25  * you're probably looking for is {@link Ext.data.Store}. If you're still interested, here's a brief description of what 
26  * AbstractStore is and is not.
27  * 
28  * AbstractStore provides the basic configuration for anything that can be considered a Store. It expects to be 
29  * given a {@link Ext.data.Model Model} that represents the type of data in the Store. It also expects to be given a 
30  * {@link Ext.data.proxy.Proxy Proxy} that handles the loading of data into the Store.
31  * 
32  * AbstractStore provides a few helpful methods such as {@link #load} and {@link #sync}, which load and save data
33  * respectively, passing the requests through the configured {@link #proxy}. Both built-in Store subclasses add extra
34  * behavior to each of these functions. Note also that each AbstractStore subclass has its own way of storing data - 
35  * in {@link Ext.data.Store} the data is saved as a flat {@link Ext.util.MixedCollection MixedCollection}, whereas in
36  * {@link Ext.data.TreeStore TreeStore} we use a {@link Ext.data.Tree} to maintain the data's hierarchy.
37  * 
38  * The store provides filtering and sorting support. This sorting/filtering can happen on the client side
39  * or can be completed on the server. This is controlled by the {@link Ext.data.Store#remoteSort remoteSort} and
40  * {@link Ext.data.Store#remoteFilter remoteFilter} config options. For more information see the {@link #sort} and
41  * {@link Ext.data.Store#filter filter} methods.
42  */
43 Ext.define('Ext.data.AbstractStore', {
44     requires: ['Ext.util.MixedCollection', 'Ext.data.Operation', 'Ext.util.Filter'],
45     
46     mixins: {
47         observable: 'Ext.util.Observable',
48         sortable: 'Ext.util.Sortable'
49     },
50     
51     statics: {
52         create: function(store){
53             if (!store.isStore) {
54                 if (!store.type) {
55                     store.type = 'store';
56                 }
57                 store = Ext.createByAlias('store.' + store.type, store);
58             }
59             return store;
60         }    
61     },
62     
63     remoteSort  : false,
64     remoteFilter: false,
65
66 <span id='Ext-data-AbstractStore-cfg-proxy'>    /**
67 </span>     * @cfg {String/Ext.data.proxy.Proxy/Object} proxy
68      * The Proxy to use for this Store. This can be either a string, a config object or a Proxy instance -
69      * see {@link #setProxy} for details.
70      */
71
72 <span id='Ext-data-AbstractStore-cfg-autoLoad'>    /**
73 </span>     * @cfg {Boolean/Object} autoLoad
74      * If data is not specified, and if autoLoad is true or an Object, this store's load method is automatically called
75      * after creation. If the value of autoLoad is an Object, this Object will be passed to the store's load method.
76      * Defaults to false.
77      */
78     autoLoad: false,
79
80 <span id='Ext-data-AbstractStore-cfg-autoSync'>    /**
81 </span>     * @cfg {Boolean} autoSync
82      * True to automatically sync the Store with its Proxy after every edit to one of its Records. Defaults to false.
83      */
84     autoSync: false,
85
86 <span id='Ext-data-AbstractStore-property-batchUpdateMode'>    /**
87 </span>     * @property {String} batchUpdateMode
88      * Sets the updating behavior based on batch synchronization. 'operation' (the default) will update the Store's
89      * internal representation of the data after each operation of the batch has completed, 'complete' will wait until
90      * the entire batch has been completed before updating the Store's data. 'complete' is a good choice for local
91      * storage proxies, 'operation' is better for remote proxies, where there is a comparatively high latency.
92      */
93     batchUpdateMode: 'operation',
94
95 <span id='Ext-data-AbstractStore-property-filterOnLoad'>    /**
96 </span>     * @property {Boolean} filterOnLoad
97      * If true, any filters attached to this Store will be run after loading data, before the datachanged event is fired.
98      * Defaults to true, ignored if {@link Ext.data.Store#remoteFilter remoteFilter} is true
99      */
100     filterOnLoad: true,
101
102 <span id='Ext-data-AbstractStore-property-sortOnLoad'>    /**
103 </span>     * @property {Boolean} sortOnLoad
104      * If true, any sorters attached to this Store will be run after loading data, before the datachanged event is fired.
105      * Defaults to true, igored if {@link Ext.data.Store#remoteSort remoteSort} is true
106      */
107     sortOnLoad: true,
108
109 <span id='Ext-data-AbstractStore-property-implicitModel'>    /**
110 </span>     * @property {Boolean} implicitModel
111      * True if a model was created implicitly for this Store. This happens if a fields array is passed to the Store's
112      * constructor instead of a model constructor or name.
113      * @private
114      */
115     implicitModel: false,
116
117 <span id='Ext-data-AbstractStore-property-defaultProxyType'>    /**
118 </span>     * @property {String} defaultProxyType
119      * The string type of the Proxy to create if none is specified. This defaults to creating a
120      * {@link Ext.data.proxy.Memory memory proxy}.
121      */
122     defaultProxyType: 'memory',
123
124 <span id='Ext-data-AbstractStore-property-isDestroyed'>    /**
125 </span>     * @property {Boolean} isDestroyed
126      * True if the Store has already been destroyed. If this is true, the reference to Store should be deleted
127      * as it will not function correctly any more.
128      */
129     isDestroyed: false,
130
131     isStore: true,
132
133 <span id='Ext-data-AbstractStore-cfg-storeId'>    /**
134 </span>     * @cfg {String} storeId
135      * Unique identifier for this store. If present, this Store will be registered with the {@link Ext.data.StoreManager},
136      * making it easy to reuse elsewhere. Defaults to undefined.
137      */
138     
139 <span id='Ext-data-AbstractStore-cfg-fields'>    /**
140 </span>     * @cfg {Object[]} fields
141      * This may be used in place of specifying a {@link #model} configuration. The fields should be a 
142      * set of {@link Ext.data.Field} configuration objects. The store will automatically create a {@link Ext.data.Model}
143      * with these fields. In general this configuration option should be avoided, it exists for the purposes of
144      * backwards compatibility. For anything more complicated, such as specifying a particular id property or
145      * assocations, a {@link Ext.data.Model} should be defined and specified for the {@link #model}
146      * config.
147      */
148
149 <span id='Ext-data-AbstractStore-cfg-model'>    /**
150 </span>     * @cfg {String} model
151      * Name of the {@link Ext.data.Model Model} associated with this store.
152      * The string is used as an argument for {@link Ext.ModelManager#getModel}.
153      */
154
155     sortRoot: 'data',
156     
157     //documented above
158     constructor: function(config) {
159         var me = this,
160             filters;
161         
162         me.addEvents(
163 <span id='Ext-data-AbstractStore-event-add'>            /**
164 </span>             * @event add
165              * Fired when a Model instance has been added to this Store
166              * @param {Ext.data.Store} store The store
167              * @param {Ext.data.Model[]} records The Model instances that were added
168              * @param {Number} index The index at which the instances were inserted
169              */
170             'add',
171
172 <span id='Ext-data-AbstractStore-event-remove'>            /**
173 </span>             * @event remove
174              * Fired when a Model instance has been removed from this Store
175              * @param {Ext.data.Store} store The Store object
176              * @param {Ext.data.Model} record The record that was removed
177              * @param {Number} index The index of the record that was removed
178              */
179             'remove',
180             
181 <span id='Ext-data-AbstractStore-event-update'>            /**
182 </span>             * @event update
183              * Fires when a Model instance has been updated
184              * @param {Ext.data.Store} this
185              * @param {Ext.data.Model} record The Model instance that was updated
186              * @param {String} operation The update operation being performed. Value may be one of:
187              *
188              *     Ext.data.Model.EDIT
189              *     Ext.data.Model.REJECT
190              *     Ext.data.Model.COMMIT
191              */
192             'update',
193
194 <span id='Ext-data-AbstractStore-event-datachanged'>            /**
195 </span>             * @event datachanged
196              * Fires whenever the records in the Store have changed in some way - this could include adding or removing
197              * records, or updating the data in existing records
198              * @param {Ext.data.Store} this The data store
199              */
200             'datachanged',
201
202 <span id='Ext-data-AbstractStore-event-beforeload'>            /**
203 </span>             * @event beforeload
204              * Fires before a request is made for a new data object. If the beforeload handler returns false the load
205              * action will be canceled.
206              * @param {Ext.data.Store} store This Store
207              * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to
208              * load the Store
209              */
210             'beforeload',
211
212 <span id='Ext-data-AbstractStore-event-load'>            /**
213 </span>             * @event load
214              * Fires whenever the store reads data from a remote data source.
215              * @param {Ext.data.Store} this
216              * @param {Ext.data.Model[]} records An array of records
217              * @param {Boolean} successful True if the operation was successful.
218              */
219             'load',
220             
221 <span id='Ext-data-AbstractStore-event-write'>            /**
222 </span>             * @event write
223              * Fires whenever a successful write has been made via the configured {@link #proxy Proxy}
224              * @param {Ext.data.Store} store This Store
225              * @param {Ext.data.Operation} operation The {@link Ext.data.Operation Operation} object that was used in
226              * the write
227              */
228             'write',
229
230 <span id='Ext-data-AbstractStore-event-beforesync'>            /**
231 </span>             * @event beforesync
232              * Fired before a call to {@link #sync} is executed. Return false from any listener to cancel the synv
233              * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy
234              */
235             'beforesync',
236 <span id='Ext-data-AbstractStore-event-clear'>            /**
237 </span>             * @event clear
238              * Fired after the {@link #removeAll} method is called.
239              * @param {Ext.data.Store} this
240              */
241             'clear'
242         );
243         
244         Ext.apply(me, config);
245         // don't use *config* anymore from here on... use *me* instead...
246
247 <span id='Ext-data-AbstractStore-property-removed'>        /**
248 </span>         * Temporary cache in which removed model instances are kept until successfully synchronised with a Proxy,
249          * at which point this is cleared.
250          * @private
251          * @property {Ext.data.Model[]} removed
252          */
253         me.removed = [];
254
255         me.mixins.observable.constructor.apply(me, arguments);
256         me.model = Ext.ModelManager.getModel(me.model);
257
258 <span id='Ext-data-AbstractStore-property-modelDefaults'>        /**
259 </span>         * @property {Object} modelDefaults
260          * @private
261          * A set of default values to be applied to every model instance added via {@link #insert} or created via {@link #create}.
262          * This is used internally by associations to set foreign keys and other fields. See the Association classes source code
263          * for examples. This should not need to be used by application developers.
264          */
265         Ext.applyIf(me, {
266             modelDefaults: {}
267         });
268
269         //Supports the 3.x style of simply passing an array of fields to the store, implicitly creating a model
270         if (!me.model &amp;&amp; me.fields) {
271             me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
272                 extend: 'Ext.data.Model',
273                 fields: me.fields,
274                 proxy: me.proxy || me.defaultProxyType
275             });
276
277             delete me.fields;
278
279             me.implicitModel = true;
280         }
281         
282         // &lt;debug&gt;
283         if (!me.model) {
284             if (Ext.isDefined(Ext.global.console)) {
285                 Ext.global.console.warn('Store defined with no model. You may have mistyped the model name.');
286             }
287         }
288         // &lt;/debug&gt;
289
290         //ensures that the Proxy is instantiated correctly
291         me.setProxy(me.proxy || me.model.getProxy());
292
293         if (me.id &amp;&amp; !me.storeId) {
294             me.storeId = me.id;
295             delete me.id;
296         }
297
298         if (me.storeId) {
299             Ext.data.StoreManager.register(me);
300         }
301         
302         me.mixins.sortable.initSortable.call(me);        
303         
304 <span id='Ext-data-AbstractStore-property-filters'>        /**
305 </span>         * @property {Ext.util.MixedCollection} filters
306          * The collection of {@link Ext.util.Filter Filters} currently applied to this Store
307          */
308         filters = me.decodeFilters(me.filters);
309         me.filters = Ext.create('Ext.util.MixedCollection');
310         me.filters.addAll(filters);
311     },
312
313 <span id='Ext-data-AbstractStore-method-setProxy'>    /**
314 </span>     * Sets the Store's Proxy by string, config object or Proxy instance
315      * @param {String/Object/Ext.data.proxy.Proxy} proxy The new Proxy, which can be either a type string, a configuration object
316      * or an Ext.data.proxy.Proxy instance
317      * @return {Ext.data.proxy.Proxy} The attached Proxy object
318      */
319     setProxy: function(proxy) {
320         var me = this;
321         
322         if (proxy instanceof Ext.data.proxy.Proxy) {
323             proxy.setModel(me.model);
324         } else {
325             if (Ext.isString(proxy)) {
326                 proxy = {
327                     type: proxy    
328                 };
329             }
330             Ext.applyIf(proxy, {
331                 model: me.model
332             });
333             
334             proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
335         }
336         
337         me.proxy = proxy;
338         
339         return me.proxy;
340     },
341
342 <span id='Ext-data-AbstractStore-method-getProxy'>    /**
343 </span>     * Returns the proxy currently attached to this proxy instance
344      * @return {Ext.data.proxy.Proxy} The Proxy instance
345      */
346     getProxy: function() {
347         return this.proxy;
348     },
349
350     //saves any phantom records
351     create: function(data, options) {
352         var me = this,
353             instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName),
354             operation;
355         
356         options = options || {};
357
358         Ext.applyIf(options, {
359             action : 'create',
360             records: [instance]
361         });
362
363         operation = Ext.create('Ext.data.Operation', options);
364
365         me.proxy.create(operation, me.onProxyWrite, me);
366         
367         return instance;
368     },
369
370     read: function() {
371         return this.load.apply(this, arguments);
372     },
373
374     onProxyRead: Ext.emptyFn,
375
376     update: function(options) {
377         var me = this,
378             operation;
379         options = options || {};
380
381         Ext.applyIf(options, {
382             action : 'update',
383             records: me.getUpdatedRecords()
384         });
385
386         operation = Ext.create('Ext.data.Operation', options);
387
388         return me.proxy.update(operation, me.onProxyWrite, me);
389     },
390
391 <span id='Ext-data-AbstractStore-method-onProxyWrite'>    /**
392 </span>     * @private
393      * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect
394      * the updates provided by the Proxy
395      */
396     onProxyWrite: function(operation) {
397         var me = this,
398             success = operation.wasSuccessful(),
399             records = operation.getRecords();
400
401         switch (operation.action) {
402             case 'create':
403                 me.onCreateRecords(records, operation, success);
404                 break;
405             case 'update':
406                 me.onUpdateRecords(records, operation, success);
407                 break;
408             case 'destroy':
409                 me.onDestroyRecords(records, operation, success);
410                 break;
411         }
412
413         if (success) {
414             me.fireEvent('write', me, operation);
415             me.fireEvent('datachanged', me);
416         }
417         //this is a callback that would have been passed to the 'create', 'update' or 'destroy' function and is optional
418         Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
419     },
420
421
422     //tells the attached proxy to destroy the given records
423     destroy: function(options) {
424         var me = this,
425             operation;
426             
427         options = options || {};
428
429         Ext.applyIf(options, {
430             action : 'destroy',
431             records: me.getRemovedRecords()
432         });
433
434         operation = Ext.create('Ext.data.Operation', options);
435
436         return me.proxy.destroy(operation, me.onProxyWrite, me);
437     },
438
439 <span id='Ext-data-AbstractStore-method-onBatchOperationComplete'>    /**
440 </span>     * @private
441      * Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default just calls through
442      * to onProxyWrite.
443      */
444     onBatchOperationComplete: function(batch, operation) {
445         return this.onProxyWrite(operation);
446     },
447
448 <span id='Ext-data-AbstractStore-method-onBatchComplete'>    /**
449 </span>     * @private
450      * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch operations
451      * and updates the Store's internal data MixedCollection.
452      */
453     onBatchComplete: function(batch, operation) {
454         var me = this,
455             operations = batch.operations,
456             length = operations.length,
457             i;
458
459         me.suspendEvents();
460
461         for (i = 0; i &lt; length; i++) {
462             me.onProxyWrite(operations[i]);
463         }
464
465         me.resumeEvents();
466
467         me.fireEvent('datachanged', me);
468     },
469
470     onBatchException: function(batch, operation) {
471         // //decide what to do... could continue with the next operation
472         // batch.start();
473         //
474         // //or retry the last operation
475         // batch.retry();
476     },
477
478 <span id='Ext-data-AbstractStore-method-filterNew'>    /**
479 </span>     * @private
480      * Filter function for new records.
481      */
482     filterNew: function(item) {
483         // only want phantom records that are valid
484         return item.phantom === true &amp;&amp; item.isValid();
485     },
486
487 <span id='Ext-data-AbstractStore-method-getNewRecords'>    /**
488 </span>     * Returns all Model instances that are either currently a phantom (e.g. have no id), or have an ID but have not
489      * yet been saved on this Store (this happens when adding a non-phantom record from another Store into this one)
490      * @return {Ext.data.Model[]} The Model instances
491      */
492     getNewRecords: function() {
493         return [];
494     },
495
496 <span id='Ext-data-AbstractStore-method-getUpdatedRecords'>    /**
497 </span>     * Returns all Model instances that have been updated in the Store but not yet synchronized with the Proxy
498      * @return {Ext.data.Model[]} The updated Model instances
499      */
500     getUpdatedRecords: function() {
501         return [];
502     },
503
504 <span id='Ext-data-AbstractStore-method-filterUpdated'>    /**
505 </span>     * @private
506      * Filter function for updated records.
507      */
508     filterUpdated: function(item) {
509         // only want dirty records, not phantoms that are valid
510         return item.dirty === true &amp;&amp; item.phantom !== true &amp;&amp; item.isValid();
511     },
512
513 <span id='Ext-data-AbstractStore-method-getRemovedRecords'>    /**
514 </span>     * Returns any records that have been removed from the store but not yet destroyed on the proxy.
515      * @return {Ext.data.Model[]} The removed Model instances
516      */
517     getRemovedRecords: function() {
518         return this.removed;
519     },
520
521     filter: function(filters, value) {
522
523     },
524
525 <span id='Ext-data-AbstractStore-method-decodeFilters'>    /**
526 </span>     * @private
527      * Normalizes an array of filter objects, ensuring that they are all Ext.util.Filter instances
528      * @param {Object[]} filters The filters array
529      * @return {Ext.util.Filter[]} Array of Ext.util.Filter objects
530      */
531     decodeFilters: function(filters) {
532         if (!Ext.isArray(filters)) {
533             if (filters === undefined) {
534                 filters = [];
535             } else {
536                 filters = [filters];
537             }
538         }
539
540         var length = filters.length,
541             Filter = Ext.util.Filter,
542             config, i;
543
544         for (i = 0; i &lt; length; i++) {
545             config = filters[i];
546
547             if (!(config instanceof Filter)) {
548                 Ext.apply(config, {
549                     root: 'data'
550                 });
551
552                 //support for 3.x style filters where a function can be defined as 'fn'
553                 if (config.fn) {
554                     config.filterFn = config.fn;
555                 }
556
557                 //support a function to be passed as a filter definition
558                 if (typeof config == 'function') {
559                     config = {
560                         filterFn: config
561                     };
562                 }
563
564                 filters[i] = new Filter(config);
565             }
566         }
567
568         return filters;
569     },
570
571     clearFilter: function(supressEvent) {
572
573     },
574
575     isFiltered: function() {
576
577     },
578
579     filterBy: function(fn, scope) {
580
581     },
582     
583 <span id='Ext-data-AbstractStore-method-sync'>    /**
584 </span>     * Synchronizes the Store with its Proxy. This asks the Proxy to batch together any new, updated
585      * and deleted records in the store, updating the Store's internal representation of the records
586      * as each operation completes.
587      */
588     sync: function() {
589         var me        = this,
590             options   = {},
591             toCreate  = me.getNewRecords(),
592             toUpdate  = me.getUpdatedRecords(),
593             toDestroy = me.getRemovedRecords(),
594             needsSync = false;
595
596         if (toCreate.length &gt; 0) {
597             options.create = toCreate;
598             needsSync = true;
599         }
600
601         if (toUpdate.length &gt; 0) {
602             options.update = toUpdate;
603             needsSync = true;
604         }
605
606         if (toDestroy.length &gt; 0) {
607             options.destroy = toDestroy;
608             needsSync = true;
609         }
610
611         if (needsSync &amp;&amp; me.fireEvent('beforesync', options) !== false) {
612             me.proxy.batch(options, me.getBatchListeners());
613         }
614     },
615
616
617 <span id='Ext-data-AbstractStore-method-getBatchListeners'>    /**
618 </span>     * @private
619      * Returns an object which is passed in as the listeners argument to proxy.batch inside this.sync.
620      * This is broken out into a separate function to allow for customisation of the listeners
621      * @return {Object} The listeners object
622      */
623     getBatchListeners: function() {
624         var me = this,
625             listeners = {
626                 scope: me,
627                 exception: me.onBatchException
628             };
629
630         if (me.batchUpdateMode == 'operation') {
631             listeners.operationcomplete = me.onBatchOperationComplete;
632         } else {
633             listeners.complete = me.onBatchComplete;
634         }
635
636         return listeners;
637     },
638
639     //deprecated, will be removed in 5.0
640     save: function() {
641         return this.sync.apply(this, arguments);
642     },
643
644 <span id='Ext-data-AbstractStore-method-load'>    /**
645 </span>     * Loads the Store using its configured {@link #proxy}.
646      * @param {Object} options (optional) config object. This is passed into the {@link Ext.data.Operation Operation}
647      * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function
648      */
649     load: function(options) {
650         var me = this,
651             operation;
652
653         options = options || {};
654
655         Ext.applyIf(options, {
656             action : 'read',
657             filters: me.filters.items,
658             sorters: me.getSorters()
659         });
660         
661         operation = Ext.create('Ext.data.Operation', options);
662
663         if (me.fireEvent('beforeload', me, operation) !== false) {
664             me.loading = true;
665             me.proxy.read(operation, me.onProxyLoad, me);
666         }
667         
668         return me;
669     },
670
671 <span id='Ext-data-AbstractStore-method-afterEdit'>    /**
672 </span>     * @private
673      * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
674      * @param {Ext.data.Model} record The model instance that was edited
675      */
676     afterEdit : function(record) {
677         var me = this;
678         
679         if (me.autoSync) {
680             me.sync();
681         }
682         
683         me.fireEvent('update', me, record, Ext.data.Model.EDIT);
684     },
685
686 <span id='Ext-data-AbstractStore-method-afterReject'>    /**
687 </span>     * @private
688      * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to..
689      * @param {Ext.data.Model} record The model instance that was edited
690      */
691     afterReject : function(record) {
692         this.fireEvent('update', this, record, Ext.data.Model.REJECT);
693     },
694
695 <span id='Ext-data-AbstractStore-method-afterCommit'>    /**
696 </span>     * @private
697      * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
698      * @param {Ext.data.Model} record The model instance that was edited
699      */
700     afterCommit : function(record) {
701         this.fireEvent('update', this, record, Ext.data.Model.COMMIT);
702     },
703
704     clearData: Ext.emptyFn,
705
706     destroyStore: function() {
707         var me = this;
708         
709         if (!me.isDestroyed) {
710             if (me.storeId) {
711                 Ext.data.StoreManager.unregister(me);
712             }
713             me.clearData();
714             me.data = null;
715             me.tree = null;
716             // Ext.destroy(this.proxy);
717             me.reader = me.writer = null;
718             me.clearListeners();
719             me.isDestroyed = true;
720
721             if (me.implicitModel) {
722                 Ext.destroy(me.model);
723             }
724         }
725     },
726     
727     doSort: function(sorterFn) {
728         var me = this;
729         if (me.remoteSort) {
730             //the load function will pick up the new sorters and request the sorted data from the proxy
731             me.load();
732         } else {
733             me.data.sortBy(sorterFn);
734             me.fireEvent('datachanged', me);
735         }
736     },
737
738     getCount: Ext.emptyFn,
739
740     getById: Ext.emptyFn,
741     
742 <span id='Ext-data-AbstractStore-method-removeAll'>    /**
743 </span>     * Removes all records from the store. This method does a &quot;fast remove&quot;,
744      * individual remove events are not called. The {@link #clear} event is
745      * fired upon completion.
746      * @method
747      */
748     removeAll: Ext.emptyFn,
749     // individual substores should implement a &quot;fast&quot; remove
750     // and fire a clear event afterwards
751
752 <span id='Ext-data-AbstractStore-method-isLoading'>    /**
753 </span>     * Returns true if the Store is currently performing a load operation
754      * @return {Boolean} True if the Store is currently loading
755      */
756     isLoading: function() {
757         return !!this.loading;
758      }
759 });
760 </pre>
761 </body>
762 </html>