Upgrade to ExtJS 3.3.1 - Released 11/30/2010
[extjs.git] / examples / docs / source / GroupSummary.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 Ext.ns('Ext.ux.grid');
16
17 <div id="cls-Ext.ux.grid.GroupSummary"></div>/**
18  * @class Ext.ux.grid.GroupSummary
19  * @extends Ext.util.Observable
20  * A GridPanel plugin that enables dynamic column calculations and a dynamically
21  * updated grouped summary row.
22  */
23 Ext.ux.grid.GroupSummary = Ext.extend(Ext.util.Observable, {
24     <div id="cfg-Ext.ux.grid.GroupSummary-summaryRenderer"></div>/**
25      * @cfg {Function} summaryRenderer Renderer example:<pre><code>
26 summaryRenderer: function(v, params, data){
27     return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)');
28 },
29      * </code></pre>
30      */
31     <div id="cfg-Ext.ux.grid.GroupSummary-summaryType"></div>/**
32      * @cfg {String} summaryType (Optional) The type of
33      * calculation to be used for the column.  For options available see
34      * {@link #Calculations}.
35      */
36
37     constructor : function(config){
38         Ext.apply(this, config);
39         Ext.ux.grid.GroupSummary.superclass.constructor.call(this);
40     },
41     init : function(grid){
42         this.grid = grid;
43         var v = this.view = grid.getView();
44         v.doGroupEnd = this.doGroupEnd.createDelegate(this);
45
46         v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
47         v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
48         v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);
49         v.afterMethod('onUpdate', this.doUpdate, this);
50         v.afterMethod('onRemove', this.doRemove, this);
51
52         if(!this.rowTpl){
53             this.rowTpl = new Ext.Template(
54                 '<div class="x-grid3-summary-row" style="{tstyle}">',
55                 '<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
56                     '<tbody><tr>{cells}</tr></tbody>',
57                 '</table></div>'
58             );
59             this.rowTpl.disableFormats = true;
60         }
61         this.rowTpl.compile();
62
63         if(!this.cellTpl){
64             this.cellTpl = new Ext.Template(
65                 '<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
66                 '<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
67                 "</td>"
68             );
69             this.cellTpl.disableFormats = true;
70         }
71         this.cellTpl.compile();
72     },
73
74     <div id="method-Ext.ux.grid.GroupSummary-toggleSummaries"></div>/**
75      * Toggle the display of the summary row on/off
76      * @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> to hide the summary.
77      */
78     toggleSummaries : function(visible){
79         var el = this.grid.getGridEl();
80         if(el){
81             if(visible === undefined){
82                 visible = el.hasClass('x-grid-hide-summary');
83             }
84             el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary');
85         }
86     },
87
88     renderSummary : function(o, cs){
89         cs = cs || this.view.getColumnData();
90         var cfg = this.grid.getColumnModel().config,
91             buf = [], c, p = {}, cf, last = cs.length-1;
92         for(var i = 0, len = cs.length; i < len; i++){
93             c = cs[i];
94             cf = cfg[i];
95             p.id = c.id;
96             p.style = c.style;
97             p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '');
98             if(cf.summaryType || cf.summaryRenderer){
99                 p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o);
100             }else{
101                 p.value = '';
102             }
103             if(p.value == undefined || p.value === "") p.value = "&#160;";
104             buf[buf.length] = this.cellTpl.apply(p);
105         }
106
107         return this.rowTpl.apply({
108             tstyle: 'width:'+this.view.getTotalWidth()+';',
109             cells: buf.join('')
110         });
111     },
112
113     /**
114      * @private
115      * @param {Object} rs
116      * @param {Object} cs
117      */
118     calculate : function(rs, cs){
119         var data = {}, r, c, cfg = this.grid.getColumnModel().config, cf;
120         for(var j = 0, jlen = rs.length; j < jlen; j++){
121             r = rs[j];
122             for(var i = 0, len = cs.length; i < len; i++){
123                 c = cs[i];
124                 cf = cfg[i];
125                 if(cf.summaryType){
126                     data[c.name] = Ext.ux.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data);
127                 }
128             }
129         }
130         return data;
131     },
132
133     doGroupEnd : function(buf, g, cs, ds, colCount){
134         var data = this.calculate(g.rs, cs);
135         buf.push('</div>', this.renderSummary({data: data}, cs), '</div>');
136     },
137
138     doWidth : function(col, w, tw){
139         if(!this.isGrouped()){
140             return;
141         }
142         var gs = this.view.getGroups(),
143             len = gs.length,
144             i = 0,
145             s;
146         for(; i < len; ++i){
147             s = gs[i].childNodes[2];
148             s.style.width = tw;
149             s.firstChild.style.width = tw;
150             s.firstChild.rows[0].childNodes[col].style.width = w;
151         }
152     },
153
154     doAllWidths : function(ws, tw){
155         if(!this.isGrouped()){
156             return;
157         }
158         var gs = this.view.getGroups(),
159             len = gs.length,
160             i = 0,
161             j, 
162             s, 
163             cells, 
164             wlen = ws.length;
165             
166         for(; i < len; i++){
167             s = gs[i].childNodes[2];
168             s.style.width = tw;
169             s.firstChild.style.width = tw;
170             cells = s.firstChild.rows[0].childNodes;
171             for(j = 0; j < wlen; j++){
172                 cells[j].style.width = ws[j];
173             }
174         }
175     },
176
177     doHidden : function(col, hidden, tw){
178         if(!this.isGrouped()){
179             return;
180         }
181         var gs = this.view.getGroups(),
182             len = gs.length,
183             i = 0,
184             s, 
185             display = hidden ? 'none' : '';
186         for(; i < len; i++){
187             s = gs[i].childNodes[2];
188             s.style.width = tw;
189             s.firstChild.style.width = tw;
190             s.firstChild.rows[0].childNodes[col].style.display = display;
191         }
192     },
193     
194     isGrouped : function(){
195         return !Ext.isEmpty(this.grid.getStore().groupField);
196     },
197
198     // Note: requires that all (or the first) record in the
199     // group share the same group value. Returns false if the group
200     // could not be found.
201     refreshSummary : function(groupValue){
202         return this.refreshSummaryById(this.view.getGroupId(groupValue));
203     },
204
205     getSummaryNode : function(gid){
206         var g = Ext.fly(gid, '_gsummary');
207         if(g){
208             return g.down('.x-grid3-summary-row', true);
209         }
210         return null;
211     },
212
213     refreshSummaryById : function(gid){
214         var g = Ext.getDom(gid);
215         if(!g){
216             return false;
217         }
218         var rs = [];
219         this.grid.getStore().each(function(r){
220             if(r._groupId == gid){
221                 rs[rs.length] = r;
222             }
223         });
224         var cs = this.view.getColumnData(),
225             data = this.calculate(rs, cs),
226             markup = this.renderSummary({data: data}, cs),
227             existing = this.getSummaryNode(gid);
228             
229         if(existing){
230             g.removeChild(existing);
231         }
232         Ext.DomHelper.append(g, markup);
233         return true;
234     },
235
236     doUpdate : function(ds, record){
237         this.refreshSummaryById(record._groupId);
238     },
239
240     doRemove : function(ds, record, index, isUpdate){
241         if(!isUpdate){
242             this.refreshSummaryById(record._groupId);
243         }
244     },
245
246     <div id="method-Ext.ux.grid.GroupSummary-showSummaryMsg"></div>/**
247      * Show a message in the summary row.
248      * <pre><code>
249 grid.on('afteredit', function(){
250     var groupValue = 'Ext Forms: Field Anchoring';
251     summary.showSummaryMsg(groupValue, 'Updating Summary...');
252 });
253      * </code></pre>
254      * @param {String} groupValue
255      * @param {String} msg Text to use as innerHTML for the summary row.
256      */
257     showSummaryMsg : function(groupValue, msg){
258         var gid = this.view.getGroupId(groupValue),
259              node = this.getSummaryNode(gid);
260         if(node){
261             node.innerHTML = '<div class="x-grid3-summary-msg">' + msg + '</div>';
262         }
263     }
264 });
265
266 //backwards compat
267 Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary;
268
269
270 <div id="prop-Ext.ux.grid.GroupSummary-Calculations"></div>/**
271  * Calculation types for summary row:</p><div class="mdetail-params"><ul>
272  * <li><b><tt>sum</tt></b> : <div class="sub-desc"></div></li>
273  * <li><b><tt>count</tt></b> : <div class="sub-desc"></div></li>
274  * <li><b><tt>max</tt></b> : <div class="sub-desc"></div></li>
275  * <li><b><tt>min</tt></b> : <div class="sub-desc"></div></li>
276  * <li><b><tt>average</tt></b> : <div class="sub-desc"></div></li>
277  * </ul></div>
278  * <p>Custom calculations may be implemented.  An example of
279  * custom <code>summaryType=totalCost</code>:</p><pre><code>
280 // define a custom summary function
281 Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){
282     return v + (record.data.estimate * record.data.rate);
283 };
284  * </code></pre>
285  * @property Calculations
286  */
287
288 Ext.ux.grid.GroupSummary.Calculations = {
289     'sum' : function(v, record, field){
290         return v + (record.data[field]||0);
291     },
292
293     'count' : function(v, record, field, data){
294         return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
295     },
296
297     'max' : function(v, record, field, data){
298         var v = record.data[field];
299         var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max'];
300         return v > max ? (data[field+'max'] = v) : max;
301     },
302
303     'min' : function(v, record, field, data){
304         var v = record.data[field];
305         var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min'];
306         return v < min ? (data[field+'min'] = v) : min;
307     },
308
309     'average' : function(v, record, field, data){
310         var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1);
311         var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0)));
312         return t === 0 ? 0 : t / c;
313     }
314 };
315 Ext.grid.GroupSummary.Calculations = Ext.ux.grid.GroupSummary.Calculations;
316
317 <div id="cls-Ext.ux.grid.HybridSummary"></div>/**
318  * @class Ext.ux.grid.HybridSummary
319  * @extends Ext.ux.grid.GroupSummary
320  * Adds capability to specify the summary data for the group via json as illustrated here:
321  * <pre><code>
322 {
323     data: [
324         {
325             projectId: 100,     project: 'House',
326             taskId:    112, description: 'Paint',
327             estimate:    6,        rate:     150,
328             due:'06/24/2007'
329         },
330         ...
331     ],
332
333     summaryData: {
334         'House': {
335             description: 14, estimate: 9,
336                    rate: 99, due: new Date(2009, 6, 29),
337                    cost: 999
338         }
339     }
340 }
341  * </code></pre>
342  *
343  */
344 Ext.ux.grid.HybridSummary = Ext.extend(Ext.ux.grid.GroupSummary, {
345     /**
346      * @private
347      * @param {Object} rs
348      * @param {Object} cs
349      */
350     calculate : function(rs, cs){
351         var gcol = this.view.getGroupField(),
352             gvalue = rs[0].data[gcol],
353             gdata = this.getSummaryData(gvalue);
354         return gdata || Ext.ux.grid.HybridSummary.superclass.calculate.call(this, rs, cs);
355     },
356
357     <div id="method-Ext.ux.grid.HybridSummary-updateSummaryData"></div>/**
358      * <pre><code>
359 grid.on('afteredit', function(){
360     var groupValue = 'Ext Forms: Field Anchoring';
361     summary.showSummaryMsg(groupValue, 'Updating Summary...');
362     setTimeout(function(){ // simulate server call
363         // HybridSummary class implements updateSummaryData
364         summary.updateSummaryData(groupValue,
365             // create data object based on configured dataIndex
366             {description: 22, estimate: 888, rate: 888, due: new Date(), cost: 8});
367     }, 2000);
368 });
369      * </code></pre>
370      * @param {String} groupValue
371      * @param {Object} data data object
372      * @param {Boolean} skipRefresh (Optional) Defaults to false
373      */
374     updateSummaryData : function(groupValue, data, skipRefresh){
375         var json = this.grid.getStore().reader.jsonData;
376         if(!json.summaryData){
377             json.summaryData = {};
378         }
379         json.summaryData[groupValue] = data;
380         if(!skipRefresh){
381             this.refreshSummary(groupValue);
382         }
383     },
384
385     <div id="method-Ext.ux.grid.HybridSummary-getSummaryData"></div>/**
386      * Returns the summaryData for the specified groupValue or null.
387      * @param {String} groupValue
388      * @return {Object} summaryData
389      */
390     getSummaryData : function(groupValue){
391         var reader = this.grid.getStore().reader,
392             json = reader.jsonData,
393             fields = reader.recordType.prototype.fields,
394             v;
395             
396         if(json && json.summaryData){
397             v = json.summaryData[groupValue];
398             if(v){
399                 return reader.extractValues(v, fields.items, fields.length);
400             }
401         }
402         return null;
403     }
404 });
405
406 //backwards compat
407 Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary;
408 </pre>    
409 </body>
410 </html>