Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / docs / source / GroupingStore.html
1 <html>
2 <head>
3   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
4   <title>The source code</title>
5     <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
6     <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
7 </head>
8 <body  onload="prettyPrint();">
9     <pre class="prettyprint lang-js">/*!
10  * Ext JS Library 3.3.1
11  * Copyright(c) 2006-2010 Sencha Inc.
12  * licensing@sencha.com
13  * http://www.sencha.com/license
14  */
15 <div id="cls-Ext.data.GroupingStore"></div>/**
16  * @class Ext.data.GroupingStore
17  * @extends Ext.data.Store
18  * A specialized store implementation that provides for grouping records by one of the available fields. This
19  * is usually used in conjunction with an {@link Ext.grid.GroupingView} to provide the data model for
20  * a grouped GridPanel.
21  *
22  * Internally, GroupingStore is simply a normal Store with multi sorting enabled from the start. The grouping field
23  * and direction are always injected as the first sorter pair. GroupingView picks up on the configured groupField and
24  * builds grid rows appropriately.
25  *
26  * @constructor
27  * Creates a new GroupingStore.
28  * @param {Object} config A config object containing the objects needed for the Store to access data,
29  * and read the data into Records.
30  * @xtype groupingstore
31  */
32 Ext.data.GroupingStore = Ext.extend(Ext.data.Store, {
33
34     //inherit docs
35     constructor: function(config) {
36         config = config || {};
37
38         //We do some preprocessing here to massage the grouping + sorting options into a single
39         //multi sort array. If grouping and sorting options are both presented to the constructor,
40         //the sorters array consists of the grouping sorter object followed by the sorting sorter object
41         //see Ext.data.Store's sorting functions for details about how multi sorting works
42         this.hasMultiSort  = true;
43         this.multiSortInfo = this.multiSortInfo || {sorters: []};
44
45         var sorters    = this.multiSortInfo.sorters,
46             groupField = config.groupField || this.groupField,
47             sortInfo   = config.sortInfo || this.sortInfo,
48             groupDir   = config.groupDir || this.groupDir;
49
50         //add the grouping sorter object first
51         if(groupField){
52             sorters.push({
53                 field    : groupField,
54                 direction: groupDir
55             });
56         }
57
58         //add the sorting sorter object if it is present
59         if (sortInfo) {
60             sorters.push(sortInfo);
61         }
62
63         Ext.data.GroupingStore.superclass.constructor.call(this, config);
64
65         this.addEvents(
66           <div id="event-Ext.data.GroupingStore-groupchange"></div>/**
67            * @event groupchange
68            * Fired whenever a call to store.groupBy successfully changes the grouping on the store
69            * @param {Ext.data.GroupingStore} store The grouping store
70            * @param {String} groupField The field that the store is now grouped by
71            */
72           'groupchange'
73         );
74
75         this.applyGroupField();
76     },
77
78     <div id="cfg-Ext.data.GroupingStore-groupField"></div>/**
79      * @cfg {String} groupField
80      * The field name by which to sort the store's data (defaults to '').
81      */
82     <div id="cfg-Ext.data.GroupingStore-remoteGroup"></div>/**
83      * @cfg {Boolean} remoteGroup
84      * True if the grouping should apply on the server side, false if it is local only (defaults to false).  If the
85      * grouping is local, it can be applied immediately to the data.  If it is remote, then it will simply act as a
86      * helper, automatically sending the grouping field name as the 'groupBy' param with each XHR call.
87      */
88     remoteGroup : false,
89     <div id="cfg-Ext.data.GroupingStore-groupOnSort"></div>/**
90      * @cfg {Boolean} groupOnSort
91      * True to sort the data on the grouping field when a grouping operation occurs, false to sort based on the
92      * existing sort info (defaults to false).
93      */
94     groupOnSort:false,
95
96     <div id="cfg-Ext.data.GroupingStore-groupDir"></div>/**
97      * @cfg {String} groupDir
98      * The direction to sort the groups. Defaults to <tt>'ASC'</tt>.
99      */
100     groupDir : 'ASC',
101
102     <div id="method-Ext.data.GroupingStore-clearGrouping"></div>/**
103      * Clears any existing grouping and refreshes the data using the default sort.
104      */
105     clearGrouping : function(){
106         this.groupField = false;
107
108         if(this.remoteGroup){
109             if(this.baseParams){
110                 delete this.baseParams.groupBy;
111                 delete this.baseParams.groupDir;
112             }
113             var lo = this.lastOptions;
114             if(lo && lo.params){
115                 delete lo.params.groupBy;
116                 delete lo.params.groupDir;
117             }
118
119             this.reload();
120         }else{
121             this.sort();
122             this.fireEvent('datachanged', this);
123         }
124     },
125
126     <div id="method-Ext.data.GroupingStore-groupBy"></div>/**
127      * Groups the data by the specified field.
128      * @param {String} field The field name by which to sort the store's data
129      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed
130      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)
131      */
132     groupBy : function(field, forceRegroup, direction) {
133         direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
134
135         if (this.groupField == field && this.groupDir == direction && !forceRegroup) {
136             return; // already grouped by this field
137         }
138
139         //check the contents of the first sorter. If the field matches the CURRENT groupField (before it is set to the new one),
140         //remove the sorter as it is actually the grouper. The new grouper is added back in by this.sort
141         var sorters = this.multiSortInfo.sorters;
142         if (sorters.length > 0 && sorters[0].field == this.groupField) {
143             sorters.shift();
144         }
145
146         this.groupField = field;
147         this.groupDir = direction;
148         this.applyGroupField();
149
150         var fireGroupEvent = function() {
151             this.fireEvent('groupchange', this, this.getGroupState());
152         };
153
154         if (this.groupOnSort) {
155             this.sort(field, direction);
156             fireGroupEvent.call(this);
157             return;
158         }
159
160         if (this.remoteGroup) {
161             this.on('load', fireGroupEvent, this, {single: true});
162             this.reload();
163         } else {
164             this.sort(sorters);
165             fireGroupEvent.call(this);
166         }
167     },
168
169     //GroupingStore always uses multisorting so we intercept calls to sort here to make sure that our grouping sorter object
170     //is always injected first.
171     sort : function(fieldName, dir) {
172         if (this.remoteSort) {
173             return Ext.data.GroupingStore.superclass.sort.call(this, fieldName, dir);
174         }
175
176         var sorters = [];
177
178         //cater for any existing valid arguments to this.sort, massage them into an array of sorter objects
179         if (Ext.isArray(arguments[0])) {
180             sorters = arguments[0];
181         } else if (fieldName == undefined) {
182             //we preserve the existing sortInfo here because this.sort is called after
183             //clearGrouping and there may be existing sorting
184             sorters = this.sortInfo ? [this.sortInfo] : [];
185         } else {
186             //TODO: this is lifted straight from Ext.data.Store's singleSort function. It should instead be
187             //refactored into a common method if possible
188             var field = this.fields.get(fieldName);
189             if (!field) return false;
190
191             var name       = field.name,
192                 sortInfo   = this.sortInfo || null,
193                 sortToggle = this.sortToggle ? this.sortToggle[name] : null;
194
195             if (!dir) {
196                 if (sortInfo && sortInfo.field == name) { // toggle sort dir
197                     dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
198                 } else {
199                     dir = field.sortDir;
200                 }
201             }
202
203             this.sortToggle[name] = dir;
204             this.sortInfo = {field: name, direction: dir};
205
206             sorters = [this.sortInfo];
207         }
208
209         //add the grouping sorter object as the first multisort sorter
210         if (this.groupField) {
211             sorters.unshift({direction: this.groupDir, field: this.groupField});
212         }
213
214         return this.multiSort.call(this, sorters, dir);
215     },
216
217     /**
218      * @private
219      * Saves the current grouping field and direction to this.baseParams and this.lastOptions.params
220      * if we're using remote grouping. Does not actually perform any grouping - just stores values
221      */
222     applyGroupField: function(){
223         if (this.remoteGroup) {
224             if(!this.baseParams){
225                 this.baseParams = {};
226             }
227
228             Ext.apply(this.baseParams, {
229                 groupBy : this.groupField,
230                 groupDir: this.groupDir
231             });
232
233             var lo = this.lastOptions;
234             if (lo && lo.params) {
235                 lo.params.groupDir = this.groupDir;
236
237                 //this is deleted because of a bug reported at http://www.extjs.com/forum/showthread.php?t=82907
238                 delete lo.params.groupBy;
239             }
240         }
241     },
242
243     /**
244      * @private
245      * TODO: This function is apparently never invoked anywhere in the framework. It has no documentation
246      * and should be considered for deletion
247      */
248     applyGrouping : function(alwaysFireChange){
249         if(this.groupField !== false){
250             this.groupBy(this.groupField, true, this.groupDir);
251             return true;
252         }else{
253             if(alwaysFireChange === true){
254                 this.fireEvent('datachanged', this);
255             }
256             return false;
257         }
258     },
259
260     /**
261      * @private
262      * Returns the grouping field that should be used. If groupOnSort is used this will be sortInfo's field,
263      * otherwise it will be this.groupField
264      * @return {String} The group field
265      */
266     getGroupState : function(){
267         return this.groupOnSort && this.groupField !== false ?
268                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;
269     }
270 });
271 Ext.reg('groupingstore', Ext.data.GroupingStore);
272 </pre>    
273 </body>
274 </html>