Upgrade to ExtJS 4.0.7 - Released 10/19/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 two 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  *     @example
51  *     Ext.define('TestResult', {
52  *         extend: 'Ext.data.Model',
53  *         fields: ['student', 'subject', {
54  *             name: 'mark',
55  *             type: 'int'
56  *         }]
57  *     });
58  *
59  *     Ext.create('Ext.grid.Panel', {
60  *         width: 200,
61  *         height: 240,
62  *         renderTo: document.body,
63  *         features: [{
64  *             groupHeaderTpl: 'Subject: {name}',
65  *             ftype: 'groupingsummary'
66  *         }],
67  *         store: {
68  *             model: 'TestResult',
69  *             groupField: 'subject',
70  *             data: [{
71  *                 student: 'Student 1',
72  *                 subject: 'Math',
73  *                 mark: 84
74  *             },{
75  *                 student: 'Student 1',
76  *                 subject: 'Science',
77  *                 mark: 72
78  *             },{
79  *                 student: 'Student 2',
80  *                 subject: 'Math',
81  *                 mark: 96
82  *             },{
83  *                 student: 'Student 2',
84  *                 subject: 'Science',
85  *                 mark: 68
86  *             }]
87  *         },
88  *         columns: [{
89  *             dataIndex: 'student',
90  *             text: 'Name',
91  *             summaryType: 'count',
92  *             summaryRenderer: function(value){
93  *                 return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : '');
94  *             }
95  *         }, {
96  *             dataIndex: 'mark',
97  *             text: 'Mark',
98  *             summaryType: 'average'
99  *         }]
100  *     });
101  */
102 Ext.define('Ext.grid.feature.GroupingSummary', {
103
104     /* Begin Definitions */
105
106     extend: 'Ext.grid.feature.Grouping',
107
108     alias: 'feature.groupingsummary',
109
110     mixins: {
111         summary: 'Ext.grid.feature.AbstractSummary'
112     },
113
114     /* End Definitions */
115
116
117    /**
118     * Modifies the row template to include the summary row.
119     * @private
120     * @return {String} The modified template
121     */
122    getFeatureTpl: function() {
123         var tpl = this.callParent(arguments);
124
125         if (this.showSummaryRow) {
126             // lop off the end </tpl> so we can attach it
127             tpl = tpl.replace('</tpl>', '');
128             tpl += '{[this.printSummaryRow(xindex)]}</tpl>';
129         }
130         return tpl;
131     },
132
133     /**
134      * Gets any fragments needed for the template.
135      * @private
136      * @return {Object} The fragments
137      */
138     getFragmentTpl: function() {
139         var me = this,
140             fragments = me.callParent();
141
142         Ext.apply(fragments, me.getSummaryFragments());
143         if (me.showSummaryRow) {
144             // this gets called before render, so we'll setup the data here.
145             me.summaryGroups = me.view.store.getGroups();
146             me.summaryData = me.generateSummaryData();
147         }
148         return fragments;
149     },
150
151     /**
152      * Gets the data for printing a template row
153      * @private
154      * @param {Number} index The index in the template
155      * @return {Array} The template values
156      */
157     getPrintData: function(index){
158         var me = this,
159             columns = me.view.headerCt.getColumnsForTpl(),
160             i = 0,
161             length = columns.length,
162             data = [],
163             name = me.summaryGroups[index - 1].name,
164             active = me.summaryData[name],
165             column;
166
167         for (; i < length; ++i) {
168             column = columns[i];
169             column.gridSummaryValue = this.getColumnValue(column, active);
170             data.push(column);
171         }
172         return data;
173     },
174
175     /**
176      * Generates all of the summary data to be used when processing the template
177      * @private
178      * @return {Object} The summary data
179      */
180     generateSummaryData: function(){
181         var me = this,
182             data = {},
183             remoteData = {},
184             store = me.view.store,
185             groupField = this.getGroupField(),
186             reader = store.proxy.reader,
187             groups = me.summaryGroups,
188             columns = me.view.headerCt.getColumnsForTpl(),
189             remote,
190             i,
191             length,
192             fieldData,
193             root,
194             key,
195             comp;
196
197         for (i = 0, length = groups.length; i < length; ++i) {
198             data[groups[i].name] = {};
199         }
200
201         /**
202          * @cfg {String} [remoteRoot=undefined]  The name of the property which contains the Array of
203          * summary objects. 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 && data[key] !== undefined) {
232                         data[key][comp.id] = remote;
233                     }
234                 }
235             }
236         }
237         return data;
238     }
239 });
240