ui-grid.grouping.js 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281
  1. /*!
  2. * ui-grid - v4.4.6 - 2018-04-06
  3. * Copyright (c) 2018 ; License: MIT
  4. */
  5. (function () {
  6. 'use strict';
  7. /**
  8. * @ngdoc overview
  9. * @name ui.grid.grouping
  10. * @description
  11. *
  12. * # ui.grid.grouping
  13. *
  14. * <div class="alert alert-warning" role="alert"><strong>Beta</strong> This feature is ready for testing, but it either hasn't seen a lot of use or has some known bugs.</div>
  15. *
  16. * This module provides grouping of rows based on the data in them, similar
  17. * in concept to excel grouping. You can group multiple columns, resulting in
  18. * nested grouping.
  19. *
  20. * In concept this feature is similar to sorting + grid footer/aggregation, it
  21. * sorts the data based on the grouped columns, then creates group rows that
  22. * reflect a break in the data. Each of those group rows can have aggregations for
  23. * the data within that group.
  24. *
  25. * This feature leverages treeBase to provide the tree functionality itself,
  26. * the key thing this feature does therefore is to set treeLevels on the rows
  27. * and insert the group headers.
  28. *
  29. * Design information:
  30. * -------------------
  31. *
  32. * Each column will get new menu items - group by, and aggregate by. Group by
  33. * will cause this column to be sorted (if not already), and will move this column
  34. * to the front of the sorted columns (i.e. grouped columns take precedence over
  35. * sorted columns). It will respect the sort order already set if there is one,
  36. * and it will allow the sorting logic to change that sort order, it just forces
  37. * the column to the front of the sorting. You can group by multiple columns, the
  38. * logic will add this column to the sorting after any already grouped columns.
  39. *
  40. * Once a grouping is defined, grouping logic is added to the rowsProcessors. This
  41. * will process the rows, identifying a break in the data value, and inserting a grouping row.
  42. * Grouping rows have specific attributes on them:
  43. *
  44. * - internalRow = true: tells us that this isn't a real row, so we can ignore it
  45. * from any processing that it looking at core data rows. This is used by the core
  46. * logic (or will be one day), as it's not grouping specific
  47. * - groupHeader = true: tells us this is a groupHeader. This is used by the grouping logic
  48. * to know if this is a groupHeader row or not
  49. *
  50. * Since the logic is baked into the rowsProcessors, it should get triggered whenever
  51. * row order or filtering or anything like that is changed. In order to avoid the row instantiation
  52. * time, and to preserve state across invocations, we hold a cache of the rows that we created
  53. * last time, and we use them again this time if we can.
  54. *
  55. * By default rows are collapsed, which means all data rows have their visible property
  56. * set to false, and only level 0 group rows are set to visible.
  57. *
  58. * <br/>
  59. * <br/>
  60. *
  61. * <div doc-module-components="ui.grid.grouping"></div>
  62. */
  63. var module = angular.module('ui.grid.grouping', ['ui.grid', 'ui.grid.treeBase']);
  64. /**
  65. * @ngdoc object
  66. * @name ui.grid.grouping.constant:uiGridGroupingConstants
  67. *
  68. * @description constants available in grouping module, this includes
  69. * all the constants declared in the treeBase module (these are manually copied
  70. * as there isn't an easy way to include constants in another constants file, and
  71. * we don't want to make users include treeBase)
  72. *
  73. */
  74. module.constant('uiGridGroupingConstants', {
  75. featureName: "grouping",
  76. rowHeaderColName: 'treeBaseRowHeaderCol',
  77. EXPANDED: 'expanded',
  78. COLLAPSED: 'collapsed',
  79. aggregation: {
  80. COUNT: 'count',
  81. SUM: 'sum',
  82. MAX: 'max',
  83. MIN: 'min',
  84. AVG: 'avg'
  85. }
  86. });
  87. /**
  88. * @ngdoc service
  89. * @name ui.grid.grouping.service:uiGridGroupingService
  90. *
  91. * @description Services for grouping features
  92. */
  93. module.service('uiGridGroupingService', ['$q', 'uiGridGroupingConstants', 'gridUtil', 'rowSorter', 'GridRow', 'gridClassFactory', 'i18nService', 'uiGridConstants', 'uiGridTreeBaseService',
  94. function ($q, uiGridGroupingConstants, gridUtil, rowSorter, GridRow, gridClassFactory, i18nService, uiGridConstants, uiGridTreeBaseService) {
  95. var service = {
  96. initializeGrid: function (grid, $scope) {
  97. uiGridTreeBaseService.initializeGrid( grid, $scope );
  98. //add feature namespace and any properties to grid for needed
  99. /**
  100. * @ngdoc object
  101. * @name ui.grid.grouping.grid:grouping
  102. *
  103. * @description Grid properties and functions added for grouping
  104. */
  105. grid.grouping = {};
  106. /**
  107. * @ngdoc property
  108. * @propertyOf ui.grid.grouping.grid:grouping
  109. * @name groupHeaderCache
  110. *
  111. * @description Cache that holds the group header rows we created last time, we'll
  112. * reuse these next time, not least because they hold our expanded states.
  113. *
  114. * We need to take care with these that they don't become a memory leak, we
  115. * create a new cache each time using the values from the old cache. This works
  116. * so long as we're creating group rows for invisible rows as well.
  117. *
  118. * The cache is a nested hash, indexed on the value we grouped by. So if we
  119. * grouped by gender then age, we'd maybe have something like:
  120. * ```
  121. * {
  122. * male: {
  123. * row: <pointer to the old row>,
  124. * children: {
  125. * 22: { row: <pointer to the old row> },
  126. * 31: { row: <pointer to the old row> }
  127. * },
  128. * female: {
  129. * row: <pointer to the old row>,
  130. * children: {
  131. * 28: { row: <pointer to the old row> },
  132. * 55: { row: <pointer to the old row> }
  133. * }
  134. * }
  135. * ```
  136. *
  137. * We create new rows for any missing rows, this means that they come in as collapsed.
  138. *
  139. */
  140. grid.grouping.groupHeaderCache = {};
  141. service.defaultGridOptions(grid.options);
  142. grid.registerRowsProcessor(service.groupRows, 400);
  143. grid.registerColumnBuilder( service.groupingColumnBuilder);
  144. grid.registerColumnsProcessor(service.groupingColumnProcessor, 400);
  145. /**
  146. * @ngdoc object
  147. * @name ui.grid.grouping.api:PublicApi
  148. *
  149. * @description Public Api for grouping feature
  150. */
  151. var publicApi = {
  152. events: {
  153. grouping: {
  154. /**
  155. * @ngdoc event
  156. * @eventOf ui.grid.grouping.api:PublicApi
  157. * @name aggregationChanged
  158. * @description raised whenever aggregation is changed, added or removed from a column
  159. *
  160. * <pre>
  161. * gridApi.grouping.on.aggregationChanged(scope,function(col){})
  162. * </pre>
  163. * @param {GridColumn} col the column which on which aggregation changed. The aggregation
  164. * type is available as `col.treeAggregation.type`
  165. */
  166. aggregationChanged: {},
  167. /**
  168. * @ngdoc event
  169. * @eventOf ui.grid.grouping.api:PublicApi
  170. * @name groupingChanged
  171. * @description raised whenever the grouped columns changes
  172. *
  173. * <pre>
  174. * gridApi.grouping.on.groupingChanged(scope,function(col){})
  175. * </pre>
  176. * @param {GridColumn} col the column which on which grouping changed. The new grouping is
  177. * available as `col.grouping`
  178. */
  179. groupingChanged: {}
  180. }
  181. },
  182. methods: {
  183. grouping: {
  184. /**
  185. * @ngdoc function
  186. * @name getGrouping
  187. * @methodOf ui.grid.grouping.api:PublicApi
  188. * @description Get the grouping configuration for this grid,
  189. * used by the saveState feature. Adds expandedState to the information
  190. * provided by the internal getGrouping, and removes any aggregations that have a source
  191. * of grouping (i.e. will be automatically reapplied when we regroup the column)
  192. * Returned grouping is an object
  193. * `{ grouping: groupArray, treeAggregations: aggregateArray, expandedState: hash }`
  194. * where grouping contains an array of objects:
  195. * `{ field: column.field, colName: column.name, groupPriority: column.grouping.groupPriority }`
  196. * and aggregations contains an array of objects:
  197. * `{ field: column.field, colName: column.name, aggregation: column.grouping.aggregation }`
  198. * and expandedState is a hash of the currently expanded nodes
  199. *
  200. * The groupArray will be sorted by groupPriority.
  201. *
  202. * @param {boolean} getExpanded whether or not to return the expanded state
  203. * @returns {object} grouping configuration
  204. */
  205. getGrouping: function ( getExpanded ) {
  206. var grouping = service.getGrouping(grid);
  207. grouping.grouping.forEach( function( group ) {
  208. group.colName = group.col.name;
  209. delete group.col;
  210. });
  211. grouping.aggregations.forEach( function( aggregation ) {
  212. aggregation.colName = aggregation.col.name;
  213. delete aggregation.col;
  214. });
  215. grouping.aggregations = grouping.aggregations.filter( function( aggregation ){
  216. return !aggregation.aggregation.source || aggregation.aggregation.source !== 'grouping';
  217. });
  218. if ( getExpanded ){
  219. grouping.rowExpandedStates = service.getRowExpandedStates( grid.grouping.groupingHeaderCache );
  220. }
  221. return grouping;
  222. },
  223. /**
  224. * @ngdoc function
  225. * @name setGrouping
  226. * @methodOf ui.grid.grouping.api:PublicApi
  227. * @description Set the grouping configuration for this grid,
  228. * used by the saveState feature, but can also be used by any
  229. * user to specify a combined grouping and aggregation configuration
  230. * @param {object} config the config you want to apply, in the format
  231. * provided out by getGrouping
  232. */
  233. setGrouping: function ( config ) {
  234. service.setGrouping(grid, config);
  235. },
  236. /**
  237. * @ngdoc function
  238. * @name groupColumn
  239. * @methodOf ui.grid.grouping.api:PublicApi
  240. * @description Adds this column to the existing grouping, at the end of the priority order.
  241. * If the column doesn't have a sort, adds one, by default ASC
  242. *
  243. * This column will move to the left of any non-group columns, the
  244. * move is handled in a columnProcessor, so gets called as part of refresh
  245. *
  246. * @param {string} columnName the name of the column we want to group
  247. */
  248. groupColumn: function( columnName ) {
  249. var column = grid.getColumn(columnName);
  250. service.groupColumn(grid, column);
  251. },
  252. /**
  253. * @ngdoc function
  254. * @name ungroupColumn
  255. * @methodOf ui.grid.grouping.api:PublicApi
  256. * @description Removes the groupPriority from this column. If the
  257. * column was previously aggregated the aggregation will come back.
  258. * The sort will remain.
  259. *
  260. * This column will move to the right of any other group columns, the
  261. * move is handled in a columnProcessor, so gets called as part of refresh
  262. *
  263. * @param {string} columnName the name of the column we want to ungroup
  264. */
  265. ungroupColumn: function( columnName ) {
  266. var column = grid.getColumn(columnName);
  267. service.ungroupColumn(grid, column);
  268. },
  269. /**
  270. * @ngdoc function
  271. * @name clearGrouping
  272. * @methodOf ui.grid.grouping.api:PublicApi
  273. * @description Clear any grouped columns and any aggregations. Doesn't remove sorting,
  274. * as we don't know whether that sorting was added by grouping or was there beforehand
  275. *
  276. */
  277. clearGrouping: function() {
  278. service.clearGrouping(grid);
  279. },
  280. /**
  281. * @ngdoc function
  282. * @name aggregateColumn
  283. * @methodOf ui.grid.grouping.api:PublicApi
  284. * @description Sets the aggregation type on a column, if the
  285. * column is currently grouped then it removes the grouping first.
  286. * If the aggregationDef is null then will result in the aggregation
  287. * being removed
  288. *
  289. * @param {string} columnName the column we want to aggregate
  290. * @param {string} or {function} aggregationDef one of the recognised types
  291. * from uiGridGroupingConstants or a custom aggregation function.
  292. * @param {string} aggregationLabel (optional) The label to use for this aggregation.
  293. */
  294. aggregateColumn: function( columnName, aggregationDef, aggregationLabel){
  295. var column = grid.getColumn(columnName);
  296. service.aggregateColumn( grid, column, aggregationDef, aggregationLabel);
  297. }
  298. }
  299. }
  300. };
  301. grid.api.registerEventsFromObject(publicApi.events);
  302. grid.api.registerMethodsFromObject(publicApi.methods);
  303. grid.api.core.on.sortChanged( $scope, service.tidyPriorities);
  304. },
  305. defaultGridOptions: function (gridOptions) {
  306. //default option to true unless it was explicitly set to false
  307. /**
  308. * @ngdoc object
  309. * @name ui.grid.grouping.api:GridOptions
  310. *
  311. * @description GridOptions for grouping feature, these are available to be
  312. * set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
  313. */
  314. /**
  315. * @ngdoc object
  316. * @name enableGrouping
  317. * @propertyOf ui.grid.grouping.api:GridOptions
  318. * @description Enable row grouping for entire grid.
  319. * <br/>Defaults to true
  320. */
  321. gridOptions.enableGrouping = gridOptions.enableGrouping !== false;
  322. /**
  323. * @ngdoc object
  324. * @name groupingShowCounts
  325. * @propertyOf ui.grid.grouping.api:GridOptions
  326. * @description shows counts on the groupHeader rows. Not that if you are using a cellFilter or a
  327. * sortingAlgorithm which relies on a specific format or data type, showing counts may cause that
  328. * to break, since the group header rows will always be a string with groupingShowCounts enabled.
  329. * <br/>Defaults to true except on columns of types 'date' and 'object'
  330. */
  331. gridOptions.groupingShowCounts = gridOptions.groupingShowCounts !== false;
  332. /**
  333. * @ngdoc object
  334. * @name groupingNullLabel
  335. * @propertyOf ui.grid.grouping.api:GridOptions
  336. * @description The string to use for the grouping header row label on rows which contain a null or undefined value in the grouped column.
  337. * <br/>Defaults to "Null"
  338. */
  339. gridOptions.groupingNullLabel = typeof(gridOptions.groupingNullLabel) === 'undefined' ? 'Null' : gridOptions.groupingNullLabel;
  340. /**
  341. * @ngdoc object
  342. * @name enableGroupHeaderSelection
  343. * @propertyOf ui.grid.grouping.api:GridOptions
  344. * @description Allows group header rows to be selected.
  345. * <br/>Defaults to false
  346. */
  347. gridOptions.enableGroupHeaderSelection = gridOptions.enableGroupHeaderSelection === true;
  348. },
  349. /**
  350. * @ngdoc function
  351. * @name groupingColumnBuilder
  352. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  353. * @description Sets the grouping defaults based on the columnDefs
  354. *
  355. * @param {object} colDef columnDef we're basing on
  356. * @param {GridColumn} col the column we're to update
  357. * @param {object} gridOptions the options we should use
  358. * @returns {promise} promise for the builder - actually we do it all inline so it's immediately resolved
  359. */
  360. groupingColumnBuilder: function (colDef, col, gridOptions) {
  361. /**
  362. * @ngdoc object
  363. * @name ui.grid.grouping.api:ColumnDef
  364. *
  365. * @description ColumnDef for grouping feature, these are available to be
  366. * set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
  367. */
  368. /**
  369. * @ngdoc object
  370. * @name enableGrouping
  371. * @propertyOf ui.grid.grouping.api:ColumnDef
  372. * @description Enable grouping on this column
  373. * <br/>Defaults to true.
  374. */
  375. if (colDef.enableGrouping === false){
  376. return;
  377. }
  378. /**
  379. * @ngdoc object
  380. * @name grouping
  381. * @propertyOf ui.grid.grouping.api:ColumnDef
  382. * @description Set the grouping for a column. Format is:
  383. * ```
  384. * {
  385. * groupPriority: <number, starts at 0, if less than 0 or undefined then we're aggregating in this column>
  386. * }
  387. * ```
  388. *
  389. * **Note that aggregation used to be included in grouping, but is now separately set on the column via treeAggregation
  390. * setting in treeBase**
  391. *
  392. * We group in the priority order given, this will also put these columns to the high order of the sort irrespective
  393. * of the sort priority given them. If there is no sort defined then we sort ascending, if there is a sort defined then
  394. * we use that sort.
  395. *
  396. * If the groupPriority is undefined or less than 0, then we expect to be aggregating, and we look at the
  397. * aggregation types to determine what sort of aggregation we can do. Values are in the constants file, but
  398. * include SUM, COUNT, MAX, MIN
  399. *
  400. * groupPriorities should generally be sequential, if they're not then the next time getGrouping is called
  401. * we'll renumber them to be sequential.
  402. * <br/>Defaults to undefined.
  403. */
  404. if ( typeof(col.grouping) === 'undefined' && typeof(colDef.grouping) !== 'undefined') {
  405. col.grouping = angular.copy(colDef.grouping);
  406. if ( typeof(col.grouping.groupPriority) !== 'undefined' && col.grouping.groupPriority > -1 ){
  407. col.treeAggregationFn = uiGridTreeBaseService.nativeAggregations()[uiGridGroupingConstants.aggregation.COUNT].aggregationFn;
  408. col.treeAggregationFinalizerFn = service.groupedFinalizerFn;
  409. }
  410. } else if (typeof(col.grouping) === 'undefined'){
  411. col.grouping = {};
  412. }
  413. if (typeof(col.grouping) !== 'undefined' && typeof(col.grouping.groupPriority) !== 'undefined' && col.grouping.groupPriority >= 0){
  414. col.suppressRemoveSort = true;
  415. }
  416. var groupColumn = {
  417. name: 'ui.grid.grouping.group',
  418. title: i18nService.get().grouping.group,
  419. icon: 'ui-grid-icon-indent-right',
  420. shown: function () {
  421. return typeof(this.context.col.grouping) === 'undefined' ||
  422. typeof(this.context.col.grouping.groupPriority) === 'undefined' ||
  423. this.context.col.grouping.groupPriority < 0;
  424. },
  425. action: function () {
  426. service.groupColumn( this.context.col.grid, this.context.col );
  427. }
  428. };
  429. var ungroupColumn = {
  430. name: 'ui.grid.grouping.ungroup',
  431. title: i18nService.get().grouping.ungroup,
  432. icon: 'ui-grid-icon-indent-left',
  433. shown: function () {
  434. return typeof(this.context.col.grouping) !== 'undefined' &&
  435. typeof(this.context.col.grouping.groupPriority) !== 'undefined' &&
  436. this.context.col.grouping.groupPriority >= 0;
  437. },
  438. action: function () {
  439. service.ungroupColumn( this.context.col.grid, this.context.col );
  440. }
  441. };
  442. var aggregateRemove = {
  443. name: 'ui.grid.grouping.aggregateRemove',
  444. title: i18nService.get().grouping.aggregate_remove,
  445. shown: function () {
  446. return typeof(this.context.col.treeAggregationFn) !== 'undefined';
  447. },
  448. action: function () {
  449. service.aggregateColumn( this.context.col.grid, this.context.col, null);
  450. }
  451. };
  452. // generic adder for the aggregation menus, which follow a pattern
  453. var addAggregationMenu = function(type, title){
  454. title = title || i18nService.get().grouping['aggregate_' + type] || type;
  455. var menuItem = {
  456. name: 'ui.grid.grouping.aggregate' + type,
  457. title: title,
  458. shown: function () {
  459. return typeof(this.context.col.treeAggregation) === 'undefined' ||
  460. typeof(this.context.col.treeAggregation.type) === 'undefined' ||
  461. this.context.col.treeAggregation.type !== type;
  462. },
  463. action: function () {
  464. service.aggregateColumn( this.context.col.grid, this.context.col, type);
  465. }
  466. };
  467. if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.grouping.aggregate' + type)) {
  468. col.menuItems.push(menuItem);
  469. }
  470. };
  471. /**
  472. * @ngdoc object
  473. * @name groupingShowGroupingMenu
  474. * @propertyOf ui.grid.grouping.api:ColumnDef
  475. * @description Show the grouping (group and ungroup items) menu on this column
  476. * <br/>Defaults to true.
  477. */
  478. if ( col.colDef.groupingShowGroupingMenu !== false ){
  479. if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.grouping.group')) {
  480. col.menuItems.push(groupColumn);
  481. }
  482. if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.grouping.ungroup')) {
  483. col.menuItems.push(ungroupColumn);
  484. }
  485. }
  486. /**
  487. * @ngdoc object
  488. * @name groupingShowAggregationMenu
  489. * @propertyOf ui.grid.grouping.api:ColumnDef
  490. * @description Show the aggregation menu on this column
  491. * <br/>Defaults to true.
  492. */
  493. if ( col.colDef.groupingShowAggregationMenu !== false ){
  494. angular.forEach(uiGridTreeBaseService.nativeAggregations(), function(aggregationDef, name){
  495. addAggregationMenu(name);
  496. });
  497. angular.forEach(gridOptions.treeCustomAggregations, function(aggregationDef, name){
  498. addAggregationMenu(name, aggregationDef.menuTitle);
  499. });
  500. if (!gridUtil.arrayContainsObjectWithProperty(col.menuItems, 'name', 'ui.grid.grouping.aggregateRemove')) {
  501. col.menuItems.push(aggregateRemove);
  502. }
  503. }
  504. },
  505. /**
  506. * @ngdoc function
  507. * @name groupingColumnProcessor
  508. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  509. * @description Moves the columns around based on which are grouped
  510. *
  511. * @param {array} columns the columns to consider rendering
  512. * @param {array} rows the grid rows, which we don't use but are passed to us
  513. * @returns {array} updated columns array
  514. */
  515. groupingColumnProcessor: function( columns, rows ) {
  516. var grid = this;
  517. columns = service.moveGroupColumns(this, columns, rows);
  518. return columns;
  519. },
  520. /**
  521. * @ngdoc function
  522. * @name groupedFinalizerFn
  523. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  524. * @description Used on group columns to display the rendered value and optionally
  525. * display the count of rows.
  526. *
  527. * @param {aggregation} the aggregation entity for a grouped column
  528. */
  529. groupedFinalizerFn: function( aggregation ){
  530. var col = this;
  531. if ( typeof(aggregation.groupVal) !== 'undefined') {
  532. aggregation.rendered = aggregation.groupVal;
  533. if ( col.grid.options.groupingShowCounts && col.colDef.type !== 'date' && col.colDef.type !== 'object' ){
  534. aggregation.rendered += (' (' + aggregation.value + ')');
  535. }
  536. } else {
  537. aggregation.rendered = null;
  538. }
  539. },
  540. /**
  541. * @ngdoc function
  542. * @name moveGroupColumns
  543. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  544. * @description Moves the column order so that the grouped columns are lined up
  545. * to the left (well, unless you're RTL, then it's the right). By doing this in
  546. * the columnsProcessor, we make it transient - when the column is ungrouped it'll
  547. * go back to where it was.
  548. *
  549. * Does nothing if the option `moveGroupColumns` is set to false.
  550. *
  551. * @param {Grid} grid grid object
  552. * @param {array} columns the columns that we should process/move
  553. * @param {array} rows the grid rows
  554. * @returns {array} updated columns
  555. */
  556. moveGroupColumns: function( grid, columns, rows ){
  557. if ( grid.options.moveGroupColumns === false){
  558. return columns;
  559. }
  560. columns.forEach( function(column, index){
  561. // position used to make stable sort in moveGroupColumns
  562. column.groupingPosition = index;
  563. });
  564. columns.sort(function(a, b){
  565. var a_group, b_group;
  566. if (a.isRowHeader){
  567. a_group = a.headerPriority;
  568. }
  569. else if ( typeof(a.grouping) === 'undefined' || typeof(a.grouping.groupPriority) === 'undefined' || a.grouping.groupPriority < 0){
  570. a_group = null;
  571. } else {
  572. a_group = a.grouping.groupPriority;
  573. }
  574. if (b.isRowHeader){
  575. b_group = b.headerPriority;
  576. }
  577. else if ( typeof(b.grouping) === 'undefined' || typeof(b.grouping.groupPriority) === 'undefined' || b.grouping.groupPriority < 0){
  578. b_group = null;
  579. } else {
  580. b_group = b.grouping.groupPriority;
  581. }
  582. // groups get sorted to the top
  583. if ( a_group !== null && b_group === null) { return -1; }
  584. if ( b_group !== null && a_group === null) { return 1; }
  585. if ( a_group !== null && b_group !== null) {return a_group - b_group; }
  586. return a.groupingPosition - b.groupingPosition;
  587. });
  588. columns.forEach( function(column, index) {
  589. delete column.groupingPosition;
  590. });
  591. return columns;
  592. },
  593. /**
  594. * @ngdoc function
  595. * @name groupColumn
  596. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  597. * @description Adds this column to the existing grouping, at the end of the priority order.
  598. * If the column doesn't have a sort, adds one, by default ASC
  599. *
  600. * This column will move to the left of any non-group columns, the
  601. * move is handled in a columnProcessor, so gets called as part of refresh
  602. *
  603. * @param {Grid} grid grid object
  604. * @param {GridColumn} column the column we want to group
  605. */
  606. groupColumn: function( grid, column){
  607. if ( typeof(column.grouping) === 'undefined' ){
  608. column.grouping = {};
  609. }
  610. // set the group priority to the next number in the hierarchy
  611. var existingGrouping = service.getGrouping( grid );
  612. column.grouping.groupPriority = existingGrouping.grouping.length;
  613. // save sort in order to restore it when column is ungrouped
  614. column.previousSort = angular.copy(column.sort);
  615. // add sort if not present
  616. if ( !column.sort ){
  617. column.sort = { direction: uiGridConstants.ASC };
  618. } else if ( typeof(column.sort.direction) === 'undefined' || column.sort.direction === null ){
  619. column.sort.direction = uiGridConstants.ASC;
  620. }
  621. column.treeAggregation = { type: uiGridGroupingConstants.aggregation.COUNT, source: 'grouping' };
  622. column.treeAggregationFn = uiGridTreeBaseService.nativeAggregations()[uiGridGroupingConstants.aggregation.COUNT].aggregationFn;
  623. column.treeAggregationFinalizerFn = service.groupedFinalizerFn;
  624. grid.api.grouping.raise.groupingChanged(column);
  625. // This indirectly calls service.tidyPriorities( grid );
  626. grid.api.core.raise.sortChanged(grid, grid.getColumnSorting());
  627. grid.queueGridRefresh();
  628. },
  629. /**
  630. * @ngdoc function
  631. * @name ungroupColumn
  632. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  633. * @description Removes the groupPriority from this column. If the
  634. * column was previously aggregated the aggregation will come back.
  635. * The sort will remain.
  636. *
  637. * This column will move to the right of any other group columns, the
  638. * move is handled in a columnProcessor, so gets called as part of refresh
  639. *
  640. * @param {Grid} grid grid object
  641. * @param {GridColumn} column the column we want to ungroup
  642. */
  643. ungroupColumn: function( grid, column){
  644. if ( typeof(column.grouping) === 'undefined' ){
  645. return;
  646. }
  647. delete column.grouping.groupPriority;
  648. delete column.treeAggregation;
  649. delete column.customTreeAggregationFinalizer;
  650. if (column.previousSort) {
  651. column.sort = column.previousSort;
  652. delete column.previousSort;
  653. }
  654. service.tidyPriorities( grid );
  655. grid.api.grouping.raise.groupingChanged(column);
  656. grid.api.core.raise.sortChanged(grid, grid.getColumnSorting());
  657. grid.queueGridRefresh();
  658. },
  659. /**
  660. * @ngdoc function
  661. * @name aggregateColumn
  662. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  663. * @description Sets the aggregation type on a column, if the
  664. * column is currently grouped then it removes the grouping first.
  665. *
  666. * @param {Grid} grid grid object
  667. * @param {GridColumn} column the column we want to aggregate
  668. * @param {string} aggregationType of the recognised types from uiGridGroupingConstants or one of the custom aggregations from gridOptions
  669. */
  670. aggregateColumn: function( grid, column, aggregationType){
  671. if (typeof(column.grouping) !== 'undefined' && typeof(column.grouping.groupPriority) !== 'undefined' && column.grouping.groupPriority >= 0){
  672. service.ungroupColumn( grid, column );
  673. }
  674. var aggregationDef = {};
  675. if ( typeof(grid.options.treeCustomAggregations[aggregationType]) !== 'undefined' ){
  676. aggregationDef = grid.options.treeCustomAggregations[aggregationType];
  677. } else if ( typeof(uiGridTreeBaseService.nativeAggregations()[aggregationType]) !== 'undefined' ){
  678. aggregationDef = uiGridTreeBaseService.nativeAggregations()[aggregationType];
  679. }
  680. column.treeAggregation = { type: aggregationType, label: i18nService.get().aggregation[aggregationDef.label] || aggregationDef.label };
  681. column.treeAggregationFn = aggregationDef.aggregationFn;
  682. column.treeAggregationFinalizerFn = aggregationDef.finalizerFn;
  683. grid.api.grouping.raise.aggregationChanged(column);
  684. grid.queueGridRefresh();
  685. },
  686. /**
  687. * @ngdoc function
  688. * @name setGrouping
  689. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  690. * @description Set the grouping based on a config object, used by the save state feature
  691. * (more specifically, by the restore function in that feature )
  692. *
  693. * @param {Grid} grid grid object
  694. * @param {object} config the config we want to set, same format as that returned by getGrouping
  695. */
  696. setGrouping: function ( grid, config ){
  697. if ( typeof(config) === 'undefined' ){
  698. return;
  699. }
  700. // first remove any existing grouping
  701. service.clearGrouping(grid);
  702. if ( config.grouping && config.grouping.length && config.grouping.length > 0 ){
  703. config.grouping.forEach( function( group ) {
  704. var col = grid.getColumn(group.colName);
  705. if ( col ) {
  706. service.groupColumn( grid, col );
  707. }
  708. });
  709. }
  710. if ( config.aggregations && config.aggregations.length ){
  711. config.aggregations.forEach( function( aggregation ) {
  712. var col = grid.getColumn(aggregation.colName);
  713. if ( col ) {
  714. service.aggregateColumn( grid, col, aggregation.aggregation.type );
  715. }
  716. });
  717. }
  718. if ( config.rowExpandedStates ){
  719. service.applyRowExpandedStates( grid.grouping.groupingHeaderCache, config.rowExpandedStates );
  720. }
  721. },
  722. /**
  723. * @ngdoc function
  724. * @name clearGrouping
  725. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  726. * @description Clear any grouped columns and any aggregations. Doesn't remove sorting,
  727. * as we don't know whether that sorting was added by grouping or was there beforehand
  728. *
  729. * @param {Grid} grid grid object
  730. */
  731. clearGrouping: function( grid ) {
  732. var currentGrouping = service.getGrouping(grid);
  733. if ( currentGrouping.grouping.length > 0 ){
  734. currentGrouping.grouping.forEach( function( group ) {
  735. if (!group.col){
  736. // should have a group.colName if there's no col
  737. group.col = grid.getColumn(group.colName);
  738. }
  739. service.ungroupColumn(grid, group.col);
  740. });
  741. }
  742. if ( currentGrouping.aggregations.length > 0 ){
  743. currentGrouping.aggregations.forEach( function( aggregation ){
  744. if (!aggregation.col){
  745. // should have a group.colName if there's no col
  746. aggregation.col = grid.getColumn(aggregation.colName);
  747. }
  748. service.aggregateColumn(grid, aggregation.col, null);
  749. });
  750. }
  751. },
  752. /**
  753. * @ngdoc function
  754. * @name tidyPriorities
  755. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  756. * @description Renumbers groupPriority and sortPriority such that
  757. * groupPriority is contiguous, and sortPriority either matches
  758. * groupPriority (for group columns), and otherwise is contiguous and
  759. * higher than groupPriority.
  760. *
  761. * @param {Grid} grid grid object
  762. */
  763. tidyPriorities: function( grid ){
  764. // if we're called from sortChanged, grid is in this, not passed as param, the param can be a column or undefined
  765. if ( ( typeof(grid) === 'undefined' || typeof(grid.grid) !== 'undefined' ) && typeof(this.grid) !== 'undefined' ) {
  766. grid = this.grid;
  767. }
  768. var groupArray = [];
  769. var sortArray = [];
  770. grid.columns.forEach( function(column, index){
  771. if ( typeof(column.grouping) !== 'undefined' && typeof(column.grouping.groupPriority) !== 'undefined' && column.grouping.groupPriority >= 0){
  772. groupArray.push(column);
  773. } else if ( typeof(column.sort) !== 'undefined' && typeof(column.sort.priority) !== 'undefined' && column.sort.priority >= 0){
  774. sortArray.push(column);
  775. }
  776. });
  777. groupArray.sort(function(a, b){ return a.grouping.groupPriority - b.grouping.groupPriority; });
  778. groupArray.forEach( function(column, index){
  779. column.grouping.groupPriority = index;
  780. column.suppressRemoveSort = true;
  781. if ( typeof(column.sort) === 'undefined'){
  782. column.sort = {};
  783. }
  784. column.sort.priority = index;
  785. });
  786. var i = groupArray.length;
  787. sortArray.sort(function(a, b){ return a.sort.priority - b.sort.priority; });
  788. sortArray.forEach( function(column, index){
  789. column.sort.priority = i;
  790. column.suppressRemoveSort = column.colDef.suppressRemoveSort;
  791. i++;
  792. });
  793. },
  794. /**
  795. * @ngdoc function
  796. * @name groupRows
  797. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  798. * @description The rowProcessor that creates the groupHeaders (i.e. does
  799. * the actual grouping).
  800. *
  801. * Assumes it is always called after the sorting processor, guaranteed by the priority setting
  802. *
  803. * Processes all the rows in order, inserting a groupHeader row whenever there is a change
  804. * in value of a grouped row, based on the sortAlgorithm used for the column. The group header row
  805. * is looked up in the groupHeaderCache, and used from there if there is one. The entity is reset
  806. * to {} if one is found.
  807. *
  808. * As it processes it maintains a `processingState` array. This records, for each level of grouping we're
  809. * working with, the following information:
  810. * ```
  811. * {
  812. * fieldName: name,
  813. * col: col,
  814. * initialised: boolean,
  815. * currentValue: value,
  816. * currentRow: gridRow,
  817. * }
  818. * ```
  819. * We look for changes in the currentValue at any of the levels. Where we find a change we:
  820. *
  821. * - create a new groupHeader row in the array
  822. *
  823. * @param {array} renderableRows the rows we want to process, usually the output from the previous rowProcessor
  824. * @returns {array} the updated rows, including our new group rows
  825. */
  826. groupRows: function( renderableRows ) {
  827. if (renderableRows.length === 0){
  828. return renderableRows;
  829. }
  830. var grid = this;
  831. grid.grouping.oldGroupingHeaderCache = grid.grouping.groupingHeaderCache || {};
  832. grid.grouping.groupingHeaderCache = {};
  833. var processingState = service.initialiseProcessingState( grid );
  834. // processes each of the fields we are grouping by, checks if the value has changed and inserts a groupHeader
  835. // Broken out as shouldn't create functions in a loop.
  836. var updateProcessingState = function( groupFieldState, stateIndex ) {
  837. var fieldValue = grid.getCellValue(row, groupFieldState.col);
  838. // look for change of value - and insert a header
  839. if ( !groupFieldState.initialised || rowSorter.getSortFn(grid, groupFieldState.col, renderableRows)(fieldValue, groupFieldState.currentValue) !== 0 ){
  840. service.insertGroupHeader( grid, renderableRows, i, processingState, stateIndex );
  841. i++;
  842. }
  843. };
  844. // use a for loop because it's tolerant of the array length changing whilst we go - we can
  845. // manipulate the iterator when we insert groupHeader rows
  846. for (var i = 0; i < renderableRows.length; i++ ){
  847. var row = renderableRows[i];
  848. if ( row.visible ){
  849. processingState.forEach( updateProcessingState );
  850. }
  851. }
  852. delete grid.grouping.oldGroupingHeaderCache;
  853. return renderableRows;
  854. },
  855. /**
  856. * @ngdoc function
  857. * @name initialiseProcessingState
  858. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  859. * @description Creates the processing state array that is used
  860. * for groupRows.
  861. *
  862. * @param {Grid} grid grid object
  863. * @returns {array} an array in the format described in the groupRows method,
  864. * initialised with blank values
  865. */
  866. initialiseProcessingState: function( grid ){
  867. var processingState = [];
  868. var columnSettings = service.getGrouping( grid );
  869. columnSettings.grouping.forEach( function( groupItem, index){
  870. processingState.push({
  871. fieldName: groupItem.field,
  872. col: groupItem.col,
  873. initialised: false,
  874. currentValue: null,
  875. currentRow: null
  876. });
  877. });
  878. return processingState;
  879. },
  880. /**
  881. * @ngdoc function
  882. * @name getGrouping
  883. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  884. * @description Get the grouping settings from the columns. As a side effect
  885. * this always renumbers the grouping starting at 0
  886. * @param {Grid} grid grid object
  887. * @returns {array} an array of the group fields, in order of priority
  888. */
  889. getGrouping: function( grid ){
  890. var groupArray = [];
  891. var aggregateArray = [];
  892. // get all the grouping
  893. grid.columns.forEach( function(column, columnIndex){
  894. if ( column.grouping ){
  895. if ( typeof(column.grouping.groupPriority) !== 'undefined' && column.grouping.groupPriority >= 0){
  896. groupArray.push({ field: column.field, col: column, groupPriority: column.grouping.groupPriority, grouping: column.grouping });
  897. }
  898. }
  899. if ( column.treeAggregation && column.treeAggregation.type ){
  900. aggregateArray.push({ field: column.field, col: column, aggregation: column.treeAggregation });
  901. }
  902. });
  903. // sort grouping into priority order
  904. groupArray.sort( function(a, b){
  905. return a.groupPriority - b.groupPriority;
  906. });
  907. // renumber the priority in case it was somewhat messed up, then remove the grouping reference
  908. groupArray.forEach( function( group, index) {
  909. group.grouping.groupPriority = index;
  910. group.groupPriority = index;
  911. delete group.grouping;
  912. });
  913. return { grouping: groupArray, aggregations: aggregateArray };
  914. },
  915. /**
  916. * @ngdoc function
  917. * @name insertGroupHeader
  918. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  919. * @description Create a group header row, and link it to the various configuration
  920. * items that we use.
  921. *
  922. * Look for the row in the oldGroupingHeaderCache, write the row into the new groupingHeaderCache.
  923. *
  924. * @param {Grid} grid grid object
  925. * @param {array} renderableRows the rows that we are processing
  926. * @param {number} rowIndex the row we were up to processing
  927. * @param {array} processingState the current processing state
  928. * @param {number} stateIndex the processing state item that we were on when we triggered a new group header -
  929. * i.e. the column that we want to create a header for
  930. */
  931. insertGroupHeader: function( grid, renderableRows, rowIndex, processingState, stateIndex ) {
  932. // set the value that caused the end of a group into the header row and the processing state
  933. var fieldName = processingState[stateIndex].fieldName;
  934. var col = processingState[stateIndex].col;
  935. var newValue = grid.getCellValue(renderableRows[rowIndex], col);
  936. var newDisplayValue = newValue;
  937. if ( typeof(newValue) === 'undefined' || newValue === null ) {
  938. newDisplayValue = grid.options.groupingNullLabel;
  939. }
  940. var getKeyAsValueForCacheMap = function(key) {
  941. if (angular.isObject(key)) {
  942. return JSON.stringify(key);
  943. } else {
  944. return key;
  945. }
  946. };
  947. var cacheItem = grid.grouping.oldGroupingHeaderCache;
  948. for ( var i = 0; i < stateIndex; i++ ){
  949. if ( cacheItem && cacheItem[getKeyAsValueForCacheMap(processingState[i].currentValue)] ){
  950. cacheItem = cacheItem[getKeyAsValueForCacheMap(processingState[i].currentValue)].children;
  951. }
  952. }
  953. var headerRow;
  954. if ( cacheItem && cacheItem[getKeyAsValueForCacheMap(newValue)]){
  955. headerRow = cacheItem[getKeyAsValueForCacheMap(newValue)].row;
  956. headerRow.entity = {};
  957. } else {
  958. headerRow = new GridRow( {}, null, grid );
  959. gridClassFactory.rowTemplateAssigner.call(grid, headerRow);
  960. }
  961. headerRow.entity['$$' + processingState[stateIndex].col.uid] = { groupVal: newDisplayValue };
  962. headerRow.treeLevel = stateIndex;
  963. headerRow.groupHeader = true;
  964. headerRow.internalRow = true;
  965. headerRow.enableCellEdit = false;
  966. headerRow.enableSelection = grid.options.enableGroupHeaderSelection;
  967. processingState[stateIndex].initialised = true;
  968. processingState[stateIndex].currentValue = newValue;
  969. processingState[stateIndex].currentRow = headerRow;
  970. // set all processing states below this one to not be initialised - change of this state
  971. // means all those need to start again
  972. service.finaliseProcessingState( processingState, stateIndex + 1);
  973. // insert our new header row
  974. renderableRows.splice(rowIndex, 0, headerRow);
  975. // add our new header row to the cache
  976. cacheItem = grid.grouping.groupingHeaderCache;
  977. for ( i = 0; i < stateIndex; i++ ){
  978. cacheItem = cacheItem[getKeyAsValueForCacheMap(processingState[i].currentValue)].children;
  979. }
  980. cacheItem[getKeyAsValueForCacheMap(newValue)] = { row: headerRow, children: {} };
  981. },
  982. /**
  983. * @ngdoc function
  984. * @name finaliseProcessingState
  985. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  986. * @description Set all processing states lower than the one that had a break in value to
  987. * no longer be initialised. Render the counts into the entity ready for display.
  988. *
  989. * @param {array} processingState the current processing state
  990. * @param {number} stateIndex the processing state item that we were on when we triggered a new group header, all
  991. * processing states after this need to be finalised
  992. */
  993. finaliseProcessingState: function( processingState, stateIndex ){
  994. for ( var i = stateIndex; i < processingState.length; i++){
  995. processingState[i].initialised = false;
  996. processingState[i].currentRow = null;
  997. processingState[i].currentValue = null;
  998. }
  999. },
  1000. /**
  1001. * @ngdoc function
  1002. * @name getRowExpandedStates
  1003. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  1004. * @description Extract the groupHeaderCache hash, pulling out only the states.
  1005. *
  1006. * The example below shows a grid that is grouped by gender then age
  1007. *
  1008. * <pre>
  1009. * {
  1010. * male: {
  1011. * state: 'expanded',
  1012. * children: {
  1013. * 22: { state: 'expanded' },
  1014. * 30: { state: 'collapsed' }
  1015. * }
  1016. * },
  1017. * female: {
  1018. * state: 'expanded',
  1019. * children: {
  1020. * 28: { state: 'expanded' },
  1021. * 55: { state: 'collapsed' }
  1022. * }
  1023. * }
  1024. * }
  1025. * </pre>
  1026. *
  1027. * @param {object} treeChildren The tree children elements object
  1028. * @returns {object} the expanded states as an object
  1029. */
  1030. getRowExpandedStates: function(treeChildren){
  1031. if ( typeof(treeChildren) === 'undefined' ){
  1032. return {};
  1033. }
  1034. var newChildren = {};
  1035. angular.forEach( treeChildren, function( value, key ){
  1036. newChildren[key] = { state: value.row.treeNode.state };
  1037. if ( value.children ){
  1038. newChildren[key].children = service.getRowExpandedStates( value.children );
  1039. } else {
  1040. newChildren[key].children = {};
  1041. }
  1042. });
  1043. return newChildren;
  1044. },
  1045. /**
  1046. * @ngdoc function
  1047. * @name applyRowExpandedStates
  1048. * @methodOf ui.grid.grouping.service:uiGridGroupingService
  1049. * @description Take a hash in the format as created by getRowExpandedStates,
  1050. * and apply it to the grid.grouping.groupHeaderCache.
  1051. *
  1052. * Takes a treeSubset, and applies to a treeSubset - so can be called
  1053. * recursively.
  1054. *
  1055. * @param {object} currentNode can be grid.grouping.groupHeaderCache, or any of
  1056. * the children of that hash
  1057. * @param {object} expandedStates can be the full expanded states, or children
  1058. * of that expanded states (which hopefully matches the subset of the groupHeaderCache)
  1059. */
  1060. applyRowExpandedStates: function( currentNode, expandedStates ){
  1061. if ( typeof(expandedStates) === 'undefined' ){
  1062. return;
  1063. }
  1064. angular.forEach(expandedStates, function( value, key ) {
  1065. if ( currentNode[key] ){
  1066. currentNode[key].row.treeNode.state = value.state;
  1067. if (value.children && currentNode[key].children){
  1068. service.applyRowExpandedStates( currentNode[key].children, value.children );
  1069. }
  1070. }
  1071. });
  1072. }
  1073. };
  1074. return service;
  1075. }]);
  1076. /**
  1077. * @ngdoc directive
  1078. * @name ui.grid.grouping.directive:uiGridGrouping
  1079. * @element div
  1080. * @restrict A
  1081. *
  1082. * @description Adds grouping features to grid
  1083. *
  1084. * @example
  1085. <example module="app">
  1086. <file name="app.js">
  1087. var app = angular.module('app', ['ui.grid', 'ui.grid.grouping']);
  1088. app.controller('MainCtrl', ['$scope', function ($scope) {
  1089. $scope.data = [
  1090. { name: 'Bob', title: 'CEO' },
  1091. { name: 'Frank', title: 'Lowly Developer' }
  1092. ];
  1093. $scope.columnDefs = [
  1094. {name: 'name', enableCellEdit: true},
  1095. {name: 'title', enableCellEdit: true}
  1096. ];
  1097. $scope.gridOptions = { columnDefs: $scope.columnDefs, data: $scope.data };
  1098. }]);
  1099. </file>
  1100. <file name="index.html">
  1101. <div ng-controller="MainCtrl">
  1102. <div ui-grid="gridOptions" ui-grid-grouping></div>
  1103. </div>
  1104. </file>
  1105. </example>
  1106. */
  1107. module.directive('uiGridGrouping', ['uiGridGroupingConstants', 'uiGridGroupingService',
  1108. function (uiGridGroupingConstants, uiGridGroupingService) {
  1109. return {
  1110. replace: true,
  1111. priority: 0,
  1112. require: '^uiGrid',
  1113. scope: false,
  1114. compile: function () {
  1115. return {
  1116. pre: function ($scope, $elm, $attrs, uiGridCtrl) {
  1117. if (uiGridCtrl.grid.options.enableGrouping !== false){
  1118. uiGridGroupingService.initializeGrid(uiGridCtrl.grid, $scope);
  1119. }
  1120. },
  1121. post: function ($scope, $elm, $attrs, uiGridCtrl) {
  1122. }
  1123. };
  1124. }
  1125. };
  1126. }]);
  1127. })();