Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / grid / feature / GroupingSummary.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.grid.feature.GroupingSummary
17  * @extends Ext.grid.feature.Grouping
18  * 
19  * This feature adds an aggregate summary row at the bottom of each group that is provided
20  * by the {@link Ext.grid.feature.Grouping} feature. There are 2 aspects to the summary:
21  * 
22  * ## Calculation
23  * 
24  * The summary value needs to be calculated for each column in the grid. This is controlled
25  * by the summaryType option specified on the column. There are several built in summary types,
26  * which can be specified as a string on the column configuration. These call underlying methods
27  * on the store:
28  *
29  *  - {@link Ext.data.Store#count count}
30  *  - {@link Ext.data.Store#sum sum}
31  *  - {@link Ext.data.Store#min min}
32  *  - {@link Ext.data.Store#max max}
33  *  - {@link Ext.data.Store#average average}
34  *
35  * Alternatively, the summaryType can be a function definition. If this is the case,
36  * the function is called with an array of records to calculate the summary value.
37  * 
38  * ## Rendering
39  * 
40  * Similar to a column, the summary also supports a summaryRenderer function. This
41  * summaryRenderer is called before displaying a value. The function is optional, if
42  * not specified the default calculated value is shown. The summaryRenderer is called with:
43  *
44  *  - value {Object} - The calculated value.
45  *  - summaryData {Object} - Contains all raw summary values for the row.
46  *  - field {String} - The name of the field we are calculating
47  * 
48  * ## Example Usage
49  *
50  *     Ext.define('TestResult', {
51  *         extend: 'Ext.data.Model',
52  *         fields: ['student', 'subject', {
53  *             name: 'mark',
54  *             type: 'int'
55  *         }]
56  *     });
57  *     
58  *     Ext.create('Ext.grid.Panel', {
59  *         width: 200,
60  *         height: 240,
61  *         renderTo: document.body,
62  *         features: [{
63  *             groupHeaderTpl: 'Subject: {name}',
64  *             ftype: 'groupingsummary'
65  *         }],
66  *         store: {
67  *             model: 'TestResult',
68  *             groupField: 'subject',
69  *             data: [{
70  *                 student: 'Student 1',
71  *                 subject: 'Math',
72  *                 mark: 84
73  *             },{
74  *                 student: 'Student 1',
75  *                 subject: 'Science',
76  *                 mark: 72
77  *             },{
78  *                 student: 'Student 2',
79  *                 subject: 'Math',
80  *                 mark: 96
81  *             },{
82  *                 student: 'Student 2',
83  *                 subject: 'Science',
84  *                 mark: 68
85  *             }]
86  *         },
87  *         columns: [{
88  *             dataIndex: 'student',
89  *             text: 'Name',
90  *             summaryType: 'count',
91  *             summaryRenderer: function(value){
92  *                 return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); 
93  *             }
94  *         }, {
95  *             dataIndex: 'mark',
96  *             text: 'Mark',
97  *             summaryType: 'average'
98  *         }]
99  *     });
100  */
101 Ext.define('Ext.grid.feature.GroupingSummary', {
102     
103     /* Begin Definitions */
104     
105     extend: 'Ext.grid.feature.Grouping',
106     
107     alias: 'feature.groupingsummary',
108     
109     mixins: {
110         summary: 'Ext.grid.feature.AbstractSummary'
111     },
112     
113     /* End Definitions */
114
115      
116    /**
117     * Modifies the row template to include the summary row.
118     * @private
119     * @return {String} The modified template
120     */
121    getFeatureTpl: function() {
122         var tpl = this.callParent(arguments);
123             
124         if (this.showSummaryRow) {
125             // lop off the end </tpl> so we can attach it
126             tpl = tpl.replace('</tpl>', '');
127             tpl += '{[this.printSummaryRow(xindex)]}</tpl>';
128         }
129         return tpl;
130     },
131     
132     /**
133      * Gets any fragments needed for the template.
134      * @private
135      * @return {Object} The fragments
136      */
137     getFragmentTpl: function() {
138         var me = this,
139             fragments = me.callParent();
140             
141         Ext.apply(fragments, me.getSummaryFragments());
142         if (me.showSummaryRow) {
143             // this gets called before render, so we'll setup the data here.
144             me.summaryGroups = me.view.store.getGroups();
145             me.summaryData = me.generateSummaryData();
146         }
147         return fragments;
148     },
149     
150     /**
151      * Gets the data for printing a template row
152      * @private
153      * @param {Number} index The index in the template
154      * @return {Array} The template values
155      */
156     getPrintData: function(index){
157         var me = this,
158             columns = me.view.headerCt.getColumnsForTpl(),
159             i = 0,
160             length = columns.length,
161             data = [],
162             name = me.summaryGroups[index - 1].name,
163             active = me.summaryData[name],
164             column;
165             
166         for (; i < length; ++i) {
167             column = columns[i];
168             column.gridSummaryValue = this.getColumnValue(column, active);
169             data.push(column);
170         }
171         return data;
172     },
173     
174     /**
175      * Generates all of the summary data to be used when processing the template
176      * @private
177      * @return {Object} The summary data
178      */
179     generateSummaryData: function(){
180         var me = this,
181             data = {},
182             remoteData = {},
183             store = me.view.store,
184             groupField = this.getGroupField(),
185             reader = store.proxy.reader,
186             groups = me.summaryGroups,
187             columns = me.view.headerCt.getColumnsForTpl(),
188             remote,
189             i,
190             length,
191             fieldData,
192             root,
193             key,
194             comp;
195             
196         for (i = 0, length = groups.length; i < length; ++i) {
197             data[groups[i].name] = {};
198         }
199         
200     /**
201      * @cfg {String} remoteRoot.  The name of the property
202      * which contains the Array of summary objects.  Defaults to <tt>undefined</tt>.
203      * It allows to use server-side calculated summaries.
204      */
205         if (me.remoteRoot && reader.rawData) {
206             // reset reader root and rebuild extractors to extract summaries data
207             root = reader.root;
208             reader.root = me.remoteRoot;
209             reader.buildExtractors(true);
210             Ext.Array.each(reader.getRoot(reader.rawData), function(value) {
211                  remoteData[value[groupField]] = value;
212             });
213             // restore initial reader configuration
214             reader.root = root;
215             reader.buildExtractors(true);
216         }
217         
218         for (i = 0, length = columns.length; i < length; ++i) {
219             comp = Ext.getCmp(columns[i].id);
220             fieldData = me.getSummary(store, comp.summaryType, comp.dataIndex, true);
221             
222             for (key in fieldData) {
223                 if (fieldData.hasOwnProperty(key)) {
224                     data[key][comp.id] = fieldData[key];
225                 }
226             }
227             
228             for (key in remoteData) {
229                 if (remoteData.hasOwnProperty(key)) {
230                     remote = remoteData[key][comp.dataIndex];
231                     if (remote !== undefined) {
232                         data[key][comp.id] = remote;
233                     }
234                 }
235             }
236         }
237         return data;
238     }
239 });
240