Upgrade to ExtJS 3.2.0 - Released 03/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.2.0
11  * Copyright(c) 2006-2010 Ext JS, Inc.
12  * licensing@extjs.com
13  * http://www.extjs.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     groupDir : 'ASC',
97
98     <div id="method-Ext.data.GroupingStore-clearGrouping"></div>/**
99      * Clears any existing grouping and refreshes the data using the default sort.
100      */
101     clearGrouping : function(){
102         this.groupField = false;
103
104         if(this.remoteGroup){
105             if(this.baseParams){
106                 delete this.baseParams.groupBy;
107                 delete this.baseParams.groupDir;
108             }
109             var lo = this.lastOptions;
110             if(lo && lo.params){
111                 delete lo.params.groupBy;
112                 delete lo.params.groupDir;
113             }
114
115             this.reload();
116         }else{
117             this.sort();
118             this.fireEvent('datachanged', this);
119         }
120     },
121
122     <div id="method-Ext.data.GroupingStore-groupBy"></div>/**
123      * Groups the data by the specified field.
124      * @param {String} field The field name by which to sort the store's data
125      * @param {Boolean} forceRegroup (optional) True to force the group to be refreshed even if the field passed
126      * in is the same as the current grouping field, false to skip grouping on the same field (defaults to false)
127      */
128     groupBy : function(field, forceRegroup, direction) {
129         direction = direction ? (String(direction).toUpperCase() == 'DESC' ? 'DESC' : 'ASC') : this.groupDir;
130
131         if (this.groupField == field && this.groupDir == direction && !forceRegroup) {
132             return; // already grouped by this field
133         }
134
135         //check the contents of the first sorter. If the field matches the CURRENT groupField (before it is set to the new one),
136         //remove the sorter as it is actually the grouper. The new grouper is added back in by this.sort
137         sorters = this.multiSortInfo.sorters;
138         if (sorters.length > 0 && sorters[0].field == this.groupField) {
139             sorters.shift();
140         }
141
142         this.groupField = field;
143         this.groupDir = direction;
144         this.applyGroupField();
145
146         var fireGroupEvent = function() {
147             this.fireEvent('groupchange', this, this.getGroupState());
148         };
149
150         if (this.groupOnSort) {
151             this.sort(field, direction);
152             fireGroupEvent.call(this);
153             return;
154         }
155
156         if (this.remoteGroup) {
157             this.on('load', fireGroupEvent, this, {single: true});
158             this.reload();
159         } else {
160             this.sort(sorters);
161             fireGroupEvent.call(this);
162         }
163     },
164
165     //GroupingStore always uses multisorting so we intercept calls to sort here to make sure that our grouping sorter object
166     //is always injected first.
167     sort : function(fieldName, dir) {
168         if (this.remoteSort) {
169             return Ext.data.GroupingStore.superclass.sort.call(this, fieldName, dir);
170         }
171
172         var sorters = [];
173
174         //cater for any existing valid arguments to this.sort, massage them into an array of sorter objects
175         if (Ext.isArray(arguments[0])) {
176             sorters = arguments[0];
177         } else if (fieldName == undefined) {
178             //we preserve the existing sortInfo here because this.sort is called after
179             //clearGrouping and there may be existing sorting
180             sorters = [this.sortInfo];
181         } else {
182             //TODO: this is lifted straight from Ext.data.Store's singleSort function. It should instead be
183             //refactored into a common method if possible
184             var field = this.fields.get(fieldName);
185             if (!field) return false;
186
187             var name       = field.name,
188                 sortInfo   = this.sortInfo || null,
189                 sortToggle = this.sortToggle ? this.sortToggle[name] : null;
190
191             if (!dir) {
192                 if (sortInfo && sortInfo.field == name) { // toggle sort dir
193                     dir = (this.sortToggle[name] || 'ASC').toggle('ASC', 'DESC');
194                 } else {
195                     dir = field.sortDir;
196                 }
197             }
198
199             this.sortToggle[name] = dir;
200             this.sortInfo = {field: name, direction: dir};
201
202             sorters = [this.sortInfo];
203         }
204
205         //add the grouping sorter object as the first multisort sorter
206         if (this.groupField) {
207             sorters.unshift({direction: this.groupDir, field: this.groupField});
208         }
209
210         return this.multiSort.call(this, sorters, dir);
211     },
212
213     /**
214      * @private
215      * Saves the current grouping field and direction to this.baseParams and this.lastOptions.params
216      * if we're using remote grouping. Does not actually perform any grouping - just stores values
217      */
218     applyGroupField: function(){
219         if (this.remoteGroup) {
220             if(!this.baseParams){
221                 this.baseParams = {};
222             }
223
224             Ext.apply(this.baseParams, {
225                 groupBy : this.groupField,
226                 groupDir: this.groupDir
227             });
228
229             var lo = this.lastOptions;
230             if (lo && lo.params) {
231                 lo.params.groupDir = this.groupDir;
232
233                 //this is deleted because of a bug reported at http://www.extjs.com/forum/showthread.php?t=82907
234                 delete lo.params.groupBy;
235             }
236         }
237     },
238
239     /**
240      * @private
241      * TODO: This function is apparently never invoked anywhere in the framework. It has no documentation
242      * and should be considered for deletion
243      */
244     applyGrouping : function(alwaysFireChange){
245         if(this.groupField !== false){
246             this.groupBy(this.groupField, true, this.groupDir);
247             return true;
248         }else{
249             if(alwaysFireChange === true){
250                 this.fireEvent('datachanged', this);
251             }
252             return false;
253         }
254     },
255
256     /**
257      * @private
258      * Returns the grouping field that should be used. If groupOnSort is used this will be sortInfo's field,
259      * otherwise it will be this.groupField
260      * @return {String} The group field
261      */
262     getGroupState : function(){
263         return this.groupOnSort && this.groupField !== false ?
264                (this.sortInfo ? this.sortInfo.field : undefined) : this.groupField;
265     }
266 });
267 Ext.reg('groupingstore', Ext.data.GroupingStore);
268 </pre>    
269 </body>
270 </html>