X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/grid/feature/GroupingSummary.js diff --git a/src/grid/feature/GroupingSummary.js b/src/grid/feature/GroupingSummary.js new file mode 100644 index 00000000..0efe6d58 --- /dev/null +++ b/src/grid/feature/GroupingSummary.js @@ -0,0 +1,218 @@ +/** + * @class Ext.grid.feature.GroupingSummary + * @extends Ext.grid.feature.Grouping + * + * This feature adds an aggregate summary row at the bottom of each group that is provided + * by the {@link Ext.grid.feature.Grouping} feature. There are 2 aspects to the summary: + * + * ## Calculation + * + * The summary value needs to be calculated for each column in the grid. This is controlled + * by the summaryType option specified on the column. There are several built in summary types, + * which can be specified as a string on the column configuration. These call underlying methods + * on the store: + * + * - {@link Ext.data.Store#count count} + * - {@link Ext.data.Store#sum sum} + * - {@link Ext.data.Store#min min} + * - {@link Ext.data.Store#max max} + * - {@link Ext.data.Store#average average} + * + * Alternatively, the summaryType can be a function definition. If this is the case, + * the function is called with an array of records to calculate the summary value. + * + * ## Rendering + * + * Similar to a column, the summary also supports a summaryRenderer function. This + * summaryRenderer is called before displaying a value. The function is optional, if + * not specified the default calculated value is shown. The summaryRenderer is called with: + * + * - value {Object} - The calculated value. + * - data {Object} - Contains all raw summary values for the row. + * - field {String} - The name of the field we are calculating + * + * ## Example Usage + * + * Ext.define('TestResult', { + * extend: 'Ext.data.Model', + * fields: ['student', 'subject', { + * name: 'mark', + * type: 'int' + * }] + * }); + * + * Ext.create('Ext.grid.Panel', { + * width: 200, + * height: 240, + * renderTo: document.body, + * features: [{ + * groupHeaderTpl: 'Subject: {name}', + * ftype: 'groupingsummary' + * }], + * store: { + * model: 'TestResult', + * groupField: 'subject', + * data: [{ + * student: 'Student 1', + * subject: 'Math', + * mark: 84 + * },{ + * student: 'Student 1', + * subject: 'Science', + * mark: 72 + * },{ + * student: 'Student 2', + * subject: 'Math', + * mark: 96 + * },{ + * student: 'Student 2', + * subject: 'Science', + * mark: 68 + * }] + * }, + * columns: [{ + * dataIndex: 'student', + * text: 'Name', + * summaryType: 'count', + * summaryRenderer: function(value){ + * return Ext.String.format('{0} student{1}', value, value !== 1 ? 's' : ''); + * } + * }, { + * dataIndex: 'mark', + * text: 'Mark', + * summaryType: 'average' + * }] + * }); + */ +Ext.define('Ext.grid.feature.GroupingSummary', { + + /* Begin Definitions */ + + extend: 'Ext.grid.feature.Grouping', + + alias: 'feature.groupingsummary', + + mixins: { + summary: 'Ext.grid.feature.AbstractSummary' + }, + + /* End Definitions */ + + + /** + * Modifies the row template to include the summary row. + * @private + * @return {String} The modified template + */ + getFeatureTpl: function() { + var tpl = this.callParent(arguments); + + if (this.showSummaryRow) { + // lop off the end so we can attach it + tpl = tpl.replace('', ''); + tpl += '{[this.printSummaryRow(xindex)]}'; + } + return tpl; + }, + + /** + * Gets any fragments needed for the template. + * @private + * @return {Object} The fragments + */ + getFragmentTpl: function() { + var me = this, + fragments = me.callParent(); + + Ext.apply(fragments, me.getSummaryFragments()); + if (me.showSummaryRow) { + // this gets called before render, so we'll setup the data here. + me.summaryGroups = me.view.store.getGroups(); + me.summaryData = me.generateSummaryData(); + } + return fragments; + }, + + /** + * Gets the data for printing a template row + * @private + * @param {Number} index The index in the template + * @return {Array} The template values + */ + getPrintData: function(index){ + var me = this, + columns = me.view.headerCt.getColumnsForTpl(), + i = 0, + length = columns.length, + data = [], + name = me.summaryGroups[index - 1].name, + active = me.summaryData[name], + column; + + for (; i < length; ++i) { + column = columns[i]; + column.gridSummaryValue = this.getColumnValue(column, active); + data.push(column); + } + return data; + }, + + /** + * Generates all of the summary data to be used when processing the template + * @private + * @return {Object} The summary data + */ + generateSummaryData: function(){ + var me = this, + data = {}, + remoteData = {}, + store = me.view.store, + groupField = this.getGroupField(), + reader = store.proxy.reader, + groups = me.summaryGroups, + columns = me.view.headerCt.getColumnsForTpl(), + i, + length, + fieldData, + root, + key, + comp; + + for (i = 0, length = groups.length; i < length; ++i) { + data[groups[i].name] = {}; + } + + /** + * @cfg {String} remoteRoot. The name of the property + * which contains the Array of summary objects. Defaults to undefined. + * It allows to use server-side calculated summaries. + */ + if (me.remoteRoot && reader.rawData) { + // reset reader root and rebuild extractors to extract summaries data + root = reader.root; + reader.root = me.remoteRoot; + reader.buildExtractors(true); + Ext.Array.each(reader.getRoot(reader.rawData), function(value) { + data[value[groupField]] = value; + data[value[groupField]]._remote = true; + }); + // restore initial reader configuration + reader.root = root; + reader.buildExtractors(true); + } + + for (i = 0, length = columns.length; i < length; ++i) { + comp = Ext.getCmp(columns[i].id); + fieldData = me.getSummary(store, comp.summaryType, comp.dataIndex, true); + + for (key in fieldData) { + if (fieldData.hasOwnProperty(key)) { + if (!data[key]._remote) { + data[key][comp.dataIndex] = fieldData[key]; + } + } + } + } + return data; + } +});