ui-grid.selection.js 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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.selection
  10. * @description
  11. *
  12. * # ui.grid.selection
  13. * This module provides row selection
  14. *
  15. * <div class="alert alert-success" role="alert"><strong>Stable</strong> This feature is stable. There should no longer be breaking api changes without a deprecation warning.</div>
  16. *
  17. * <div doc-module-components="ui.grid.selection"></div>
  18. */
  19. var module = angular.module('ui.grid.selection', ['ui.grid']);
  20. /**
  21. * @ngdoc object
  22. * @name ui.grid.selection.constant:uiGridSelectionConstants
  23. *
  24. * @description constants available in selection module
  25. */
  26. module.constant('uiGridSelectionConstants', {
  27. featureName: "selection",
  28. selectionRowHeaderColName: 'selectionRowHeaderCol'
  29. });
  30. //add methods to GridRow
  31. angular.module('ui.grid').config(['$provide', function ($provide) {
  32. $provide.decorator('GridRow', ['$delegate', function ($delegate) {
  33. /**
  34. * @ngdoc object
  35. * @name ui.grid.selection.api:GridRow
  36. *
  37. * @description GridRow prototype functions added for selection
  38. */
  39. /**
  40. * @ngdoc object
  41. * @name enableSelection
  42. * @propertyOf ui.grid.selection.api:GridRow
  43. * @description Enable row selection for this row, only settable by internal code.
  44. *
  45. * The grouping feature, for example, might set group header rows to not be selectable.
  46. * <br/>Defaults to true
  47. */
  48. /**
  49. * @ngdoc object
  50. * @name isSelected
  51. * @propertyOf ui.grid.selection.api:GridRow
  52. * @description Selected state of row. Should be readonly. Make any changes to selected state using setSelected().
  53. * <br/>Defaults to false
  54. */
  55. /**
  56. * @ngdoc function
  57. * @name setSelected
  58. * @methodOf ui.grid.selection.api:GridRow
  59. * @description Sets the isSelected property and updates the selectedCount
  60. * Changes to isSelected state should only be made via this function
  61. * @param {bool} selected value to set
  62. */
  63. $delegate.prototype.setSelected = function (selected) {
  64. if (selected !== this.isSelected) {
  65. this.isSelected = selected;
  66. this.grid.selection.selectedCount += selected ? 1 : -1;
  67. }
  68. };
  69. return $delegate;
  70. }]);
  71. }]);
  72. /**
  73. * @ngdoc service
  74. * @name ui.grid.selection.service:uiGridSelectionService
  75. *
  76. * @description Services for selection features
  77. */
  78. module.service('uiGridSelectionService', ['$q', '$templateCache', 'uiGridSelectionConstants', 'gridUtil',
  79. function ($q, $templateCache, uiGridSelectionConstants, gridUtil) {
  80. var service = {
  81. initializeGrid: function (grid) {
  82. //add feature namespace and any properties to grid for needed
  83. /**
  84. * @ngdoc object
  85. * @name ui.grid.selection.grid:selection
  86. *
  87. * @description Grid properties and functions added for selection
  88. */
  89. grid.selection = {};
  90. grid.selection.lastSelectedRow = null;
  91. grid.selection.selectAll = false;
  92. /**
  93. * @ngdoc object
  94. * @name selectedCount
  95. * @propertyOf ui.grid.selection.grid:selection
  96. * @description Current count of selected rows
  97. * @example
  98. * var count = grid.selection.selectedCount
  99. */
  100. grid.selection.selectedCount = 0;
  101. service.defaultGridOptions(grid.options);
  102. /**
  103. * @ngdoc object
  104. * @name ui.grid.selection.api:PublicApi
  105. *
  106. * @description Public Api for selection feature
  107. */
  108. var publicApi = {
  109. events: {
  110. selection: {
  111. /**
  112. * @ngdoc event
  113. * @name rowSelectionChanged
  114. * @eventOf ui.grid.selection.api:PublicApi
  115. * @description is raised after the row.isSelected state is changed
  116. * @param {object} scope the scope associated with the grid
  117. * @param {GridRow} row the row that was selected/deselected
  118. * @param {Event} evt object if raised from an event
  119. */
  120. rowSelectionChanged: function (scope, row, evt) {
  121. },
  122. /**
  123. * @ngdoc event
  124. * @name rowSelectionChangedBatch
  125. * @eventOf ui.grid.selection.api:PublicApi
  126. * @description is raised after the row.isSelected state is changed
  127. * in bulk, if the `enableSelectionBatchEvent` option is set to true
  128. * (which it is by default). This allows more efficient processing
  129. * of bulk events.
  130. * @param {object} scope the scope associated with the grid
  131. * @param {array} rows the rows that were selected/deselected
  132. * @param {Event} evt object if raised from an event
  133. */
  134. rowSelectionChangedBatch: function (scope, rows, evt) {
  135. }
  136. }
  137. },
  138. methods: {
  139. selection: {
  140. /**
  141. * @ngdoc function
  142. * @name toggleRowSelection
  143. * @methodOf ui.grid.selection.api:PublicApi
  144. * @description Toggles data row as selected or unselected
  145. * @param {object} rowEntity gridOptions.data[] array instance
  146. * @param {Event} evt object if raised from an event
  147. */
  148. toggleRowSelection: function (rowEntity, evt) {
  149. var row = grid.getRow(rowEntity);
  150. if (row !== null) {
  151. service.toggleRowSelection(grid, row, evt, grid.options.multiSelect, grid.options.noUnselect);
  152. }
  153. },
  154. /**
  155. * @ngdoc function
  156. * @name selectRow
  157. * @methodOf ui.grid.selection.api:PublicApi
  158. * @description Select the data row
  159. * @param {object} rowEntity gridOptions.data[] array instance
  160. * @param {Event} evt object if raised from an event
  161. */
  162. selectRow: function (rowEntity, evt) {
  163. var row = grid.getRow(rowEntity);
  164. if (row !== null && !row.isSelected) {
  165. service.toggleRowSelection(grid, row, evt, grid.options.multiSelect, grid.options.noUnselect);
  166. }
  167. },
  168. /**
  169. * @ngdoc function
  170. * @name selectRowByVisibleIndex
  171. * @methodOf ui.grid.selection.api:PublicApi
  172. * @description Select the specified row by visible index (i.e. if you
  173. * specify row 0 you'll get the first visible row selected). In this context
  174. * visible means of those rows that are theoretically visible (i.e. not filtered),
  175. * rather than rows currently rendered on the screen.
  176. * @param {number} rowNum index within the rowsVisible array
  177. * @param {Event} evt object if raised from an event
  178. */
  179. selectRowByVisibleIndex: function (rowNum, evt) {
  180. var row = grid.renderContainers.body.visibleRowCache[rowNum];
  181. if (row !== null && typeof (row) !== 'undefined' && !row.isSelected) {
  182. service.toggleRowSelection(grid, row, evt, grid.options.multiSelect, grid.options.noUnselect);
  183. }
  184. },
  185. /**
  186. * @ngdoc function
  187. * @name unSelectRow
  188. * @methodOf ui.grid.selection.api:PublicApi
  189. * @description UnSelect the data row
  190. * @param {object} rowEntity gridOptions.data[] array instance
  191. * @param {Event} evt object if raised from an event
  192. */
  193. unSelectRow: function (rowEntity, evt) {
  194. var row = grid.getRow(rowEntity);
  195. if (row !== null && row.isSelected) {
  196. service.toggleRowSelection(grid, row, evt, grid.options.multiSelect, grid.options.noUnselect);
  197. }
  198. },
  199. /**
  200. * @ngdoc function
  201. * @name unSelectRowByVisibleIndex
  202. * @methodOf ui.grid.selection.api:PublicApi
  203. * @description Unselect the specified row by visible index (i.e. if you
  204. * specify row 0 you'll get the first visible row unselected). In this context
  205. * visible means of those rows that are theoretically visible (i.e. not filtered),
  206. * rather than rows currently rendered on the screen.
  207. * @param {number} rowNum index within the rowsVisible array
  208. * @param {Event} evt object if raised from an event
  209. */
  210. unSelectRowByVisibleIndex: function (rowNum, evt) {
  211. var row = grid.renderContainers.body.visibleRowCache[rowNum];
  212. if (row !== null && typeof (row) !== 'undefined' && row.isSelected) {
  213. service.toggleRowSelection(grid, row, evt, grid.options.multiSelect, grid.options.noUnselect);
  214. }
  215. },
  216. /**
  217. * @ngdoc function
  218. * @name selectAllRows
  219. * @methodOf ui.grid.selection.api:PublicApi
  220. * @description Selects all rows. Does nothing if multiSelect = false
  221. * @param {Event} evt object if raised from an event
  222. */
  223. selectAllRows: function (evt) {
  224. if (grid.options.multiSelect !== false) {
  225. var changedRows = [];
  226. grid.rows.forEach(function (row) {
  227. if (!row.isSelected && row.enableSelection !== false && grid.options.isRowSelectable(row) !== false) {
  228. row.setSelected(true);
  229. service.decideRaiseSelectionEvent(grid, row, changedRows, evt);
  230. }
  231. });
  232. service.decideRaiseSelectionBatchEvent(grid, changedRows, evt);
  233. grid.selection.selectAll = true;
  234. }
  235. },
  236. /**
  237. * @ngdoc function
  238. * @name selectAllVisibleRows
  239. * @methodOf ui.grid.selection.api:PublicApi
  240. * @description Selects all visible rows. Does nothing if multiSelect = false
  241. * @param {Event} evt object if raised from an event
  242. */
  243. selectAllVisibleRows: function (evt) {
  244. if (grid.options.multiSelect !== false) {
  245. var changedRows = [];
  246. grid.rows.forEach(function(row) {
  247. if (row.visible) {
  248. if (!row.isSelected && row.enableSelection !== false && grid.options.isRowSelectable(row) !== false) {
  249. row.setSelected(true);
  250. service.decideRaiseSelectionEvent(grid, row, changedRows, evt);
  251. }
  252. } else if (row.isSelected) {
  253. row.setSelected(false);
  254. service.decideRaiseSelectionEvent(grid, row, changedRows, evt);
  255. }
  256. });
  257. service.decideRaiseSelectionBatchEvent(grid, changedRows, evt);
  258. grid.selection.selectAll = true;
  259. }
  260. },
  261. /**
  262. * @ngdoc function
  263. * @name clearSelectedRows
  264. * @methodOf ui.grid.selection.api:PublicApi
  265. * @description Unselects all rows
  266. * @param {Event} evt object if raised from an event
  267. */
  268. clearSelectedRows: function (evt) {
  269. service.clearSelectedRows(grid, evt);
  270. },
  271. /**
  272. * @ngdoc function
  273. * @name getSelectedRows
  274. * @methodOf ui.grid.selection.api:PublicApi
  275. * @description returns all selectedRow's entity references
  276. */
  277. getSelectedRows: function () {
  278. return service.getSelectedRows(grid).map(function (gridRow) {
  279. return gridRow.entity;
  280. }).filter(function (entity) {
  281. return entity.hasOwnProperty('$$hashKey');
  282. });
  283. },
  284. /**
  285. * @ngdoc function
  286. * @name getSelectedGridRows
  287. * @methodOf ui.grid.selection.api:PublicApi
  288. * @description returns all selectedRow's as gridRows
  289. */
  290. getSelectedGridRows: function () {
  291. return service.getSelectedRows(grid);
  292. },
  293. /**
  294. * @ngdoc function
  295. * @name getSelectedCount
  296. * @methodOf ui.grid.selection.api:PublicApi
  297. * @description returns the number of rows selected
  298. */
  299. getSelectedCount: function () {
  300. return grid.selection.selectedCount;
  301. },
  302. /**
  303. * @ngdoc function
  304. * @name setMultiSelect
  305. * @methodOf ui.grid.selection.api:PublicApi
  306. * @description Sets the current gridOption.multiSelect to true or false
  307. * @param {bool} multiSelect true to allow multiple rows
  308. */
  309. setMultiSelect: function (multiSelect) {
  310. grid.options.multiSelect = multiSelect;
  311. },
  312. /**
  313. * @ngdoc function
  314. * @name setModifierKeysToMultiSelect
  315. * @methodOf ui.grid.selection.api:PublicApi
  316. * @description Sets the current gridOption.modifierKeysToMultiSelect to true or false
  317. * @param {bool} modifierKeysToMultiSelect true to only allow multiple rows when using ctrlKey or shiftKey is used
  318. */
  319. setModifierKeysToMultiSelect: function (modifierKeysToMultiSelect) {
  320. grid.options.modifierKeysToMultiSelect = modifierKeysToMultiSelect;
  321. },
  322. /**
  323. * @ngdoc function
  324. * @name getSelectAllState
  325. * @methodOf ui.grid.selection.api:PublicApi
  326. * @description Returns whether or not the selectAll checkbox is currently ticked. The
  327. * grid doesn't automatically select rows when you add extra data - so when you add data
  328. * you need to explicitly check whether the selectAll is set, and then call setVisible rows
  329. * if it is
  330. */
  331. getSelectAllState: function () {
  332. return grid.selection.selectAll;
  333. }
  334. }
  335. }
  336. };
  337. grid.api.registerEventsFromObject(publicApi.events);
  338. grid.api.registerMethodsFromObject(publicApi.methods);
  339. },
  340. defaultGridOptions: function (gridOptions) {
  341. //default option to true unless it was explicitly set to false
  342. /**
  343. * @ngdoc object
  344. * @name ui.grid.selection.api:GridOptions
  345. *
  346. * @description GridOptions for selection feature, these are available to be
  347. * set using the ui-grid {@link ui.grid.class:GridOptions gridOptions}
  348. */
  349. /**
  350. * @ngdoc object
  351. * @name enableRowSelection
  352. * @propertyOf ui.grid.selection.api:GridOptions
  353. * @description Enable row selection for entire grid.
  354. * <br/>Defaults to true
  355. */
  356. gridOptions.enableRowSelection = gridOptions.enableRowSelection !== false;
  357. /**
  358. * @ngdoc object
  359. * @name multiSelect
  360. * @propertyOf ui.grid.selection.api:GridOptions
  361. * @description Enable multiple row selection for entire grid
  362. * <br/>Defaults to true
  363. */
  364. gridOptions.multiSelect = gridOptions.multiSelect !== false;
  365. /**
  366. * @ngdoc object
  367. * @name noUnselect
  368. * @propertyOf ui.grid.selection.api:GridOptions
  369. * @description Prevent a row from being unselected. Works in conjunction
  370. * with `multiselect = false` and `gridApi.selection.selectRow()` to allow
  371. * you to create a single selection only grid - a row is always selected, you
  372. * can only select different rows, you can't unselect the row.
  373. * <br/>Defaults to false
  374. */
  375. gridOptions.noUnselect = gridOptions.noUnselect === true;
  376. /**
  377. * @ngdoc object
  378. * @name modifierKeysToMultiSelect
  379. * @propertyOf ui.grid.selection.api:GridOptions
  380. * @description Enable multiple row selection only when using the ctrlKey or shiftKey. Requires multiSelect to be true.
  381. * <br/>Defaults to false
  382. */
  383. gridOptions.modifierKeysToMultiSelect = gridOptions.modifierKeysToMultiSelect === true;
  384. /**
  385. * @ngdoc object
  386. * @name enableRowHeaderSelection
  387. * @propertyOf ui.grid.selection.api:GridOptions
  388. * @description Enable a row header to be used for selection
  389. * <br/>Defaults to true
  390. */
  391. gridOptions.enableRowHeaderSelection = gridOptions.enableRowHeaderSelection !== false;
  392. /**
  393. * @ngdoc object
  394. * @name enableFullRowSelection
  395. * @propertyOf ui.grid.selection.api:GridOptions
  396. * @description Enable selection by clicking anywhere on the row. Defaults to
  397. * false if `enableRowHeaderSelection` is true, otherwise defaults to false.
  398. */
  399. if (typeof (gridOptions.enableFullRowSelection) === 'undefined') {
  400. gridOptions.enableFullRowSelection = !gridOptions.enableRowHeaderSelection;
  401. }
  402. /**
  403. * @ngdoc object
  404. * @name enableSelectAll
  405. * @propertyOf ui.grid.selection.api:GridOptions
  406. * @description Enable the select all checkbox at the top of the selectionRowHeader
  407. * <br/>Defaults to true
  408. */
  409. gridOptions.enableSelectAll = gridOptions.enableSelectAll !== false;
  410. /**
  411. * @ngdoc object
  412. * @name enableSelectionBatchEvent
  413. * @propertyOf ui.grid.selection.api:GridOptions
  414. * @description If selected rows are changed in bulk, either via the API or
  415. * via the selectAll checkbox, then a separate event is fired. Setting this
  416. * option to false will cause the rowSelectionChanged event to be called multiple times
  417. * instead
  418. * <br/>Defaults to true
  419. */
  420. gridOptions.enableSelectionBatchEvent = gridOptions.enableSelectionBatchEvent !== false;
  421. /**
  422. * @ngdoc object
  423. * @name selectionRowHeaderWidth
  424. * @propertyOf ui.grid.selection.api:GridOptions
  425. * @description can be used to set a custom width for the row header selection column
  426. * <br/>Defaults to 30px
  427. */
  428. gridOptions.selectionRowHeaderWidth = angular.isDefined(gridOptions.selectionRowHeaderWidth) ? gridOptions.selectionRowHeaderWidth : 30;
  429. /**
  430. * @ngdoc object
  431. * @name enableFooterTotalSelected
  432. * @propertyOf ui.grid.selection.api:GridOptions
  433. * @description Shows the total number of selected items in footer if true.
  434. * <br/>Defaults to true.
  435. * <br/>GridOptions.showGridFooter must also be set to true.
  436. */
  437. gridOptions.enableFooterTotalSelected = gridOptions.enableFooterTotalSelected !== false;
  438. /**
  439. * @ngdoc object
  440. * @name isRowSelectable
  441. * @propertyOf ui.grid.selection.api:GridOptions
  442. * @description Makes it possible to specify a method that evaluates for each row and sets its "enableSelection" property.
  443. */
  444. gridOptions.isRowSelectable = angular.isDefined(gridOptions.isRowSelectable) ? gridOptions.isRowSelectable : angular.noop;
  445. },
  446. /**
  447. * @ngdoc function
  448. * @name toggleRowSelection
  449. * @methodOf ui.grid.selection.service:uiGridSelectionService
  450. * @description Toggles row as selected or unselected
  451. * @param {Grid} grid grid object
  452. * @param {GridRow} row row to select or deselect
  453. * @param {Event} evt object if resulting from event
  454. * @param {bool} multiSelect if false, only one row at time can be selected
  455. * @param {bool} noUnselect if true then rows cannot be unselected
  456. */
  457. toggleRowSelection: function (grid, row, evt, multiSelect, noUnselect) {
  458. var selected = row.isSelected;
  459. if (row.enableSelection === false) {
  460. return;
  461. }
  462. var selectedRows;
  463. if (!multiSelect && !selected) {
  464. service.clearSelectedRows(grid, evt);
  465. } else if (!multiSelect && selected) {
  466. selectedRows = service.getSelectedRows(grid);
  467. if (selectedRows.length > 1) {
  468. selected = false; // Enable reselect of the row
  469. service.clearSelectedRows(grid, evt);
  470. }
  471. }
  472. if (selected && noUnselect) {
  473. // don't deselect the row
  474. } else {
  475. row.setSelected(!selected);
  476. if (row.isSelected === true) {
  477. grid.selection.lastSelectedRow = row;
  478. }
  479. selectedRows = service.getSelectedRows(grid);
  480. grid.selection.selectAll = grid.rows.length === selectedRows.length;
  481. grid.api.selection.raise.rowSelectionChanged(row, evt);
  482. }
  483. },
  484. /**
  485. * @ngdoc function
  486. * @name shiftSelect
  487. * @methodOf ui.grid.selection.service:uiGridSelectionService
  488. * @description selects a group of rows from the last selected row using the shift key
  489. * @param {Grid} grid grid object
  490. * @param {GridRow} row clicked row
  491. * @param {Event} evt object if raised from an event
  492. * @param {bool} multiSelect if false, does nothing this is for multiSelect only
  493. */
  494. shiftSelect: function (grid, row, evt, multiSelect) {
  495. if (!multiSelect) {
  496. return;
  497. }
  498. var selectedRows = service.getSelectedRows(grid);
  499. var fromRow = selectedRows.length > 0 ? grid.renderContainers.body.visibleRowCache.indexOf(grid.selection.lastSelectedRow) : 0;
  500. var toRow = grid.renderContainers.body.visibleRowCache.indexOf(row);
  501. //reverse select direction
  502. if (fromRow > toRow) {
  503. var tmp = fromRow;
  504. fromRow = toRow;
  505. toRow = tmp;
  506. }
  507. var changedRows = [];
  508. for (var i = fromRow; i <= toRow; i++) {
  509. var rowToSelect = grid.renderContainers.body.visibleRowCache[i];
  510. if (rowToSelect) {
  511. if (!rowToSelect.isSelected && rowToSelect.enableSelection !== false) {
  512. rowToSelect.setSelected(true);
  513. grid.selection.lastSelectedRow = rowToSelect;
  514. service.decideRaiseSelectionEvent(grid, rowToSelect, changedRows, evt);
  515. }
  516. }
  517. }
  518. service.decideRaiseSelectionBatchEvent(grid, changedRows, evt);
  519. },
  520. /**
  521. * @ngdoc function
  522. * @name getSelectedRows
  523. * @methodOf ui.grid.selection.service:uiGridSelectionService
  524. * @description Returns all the selected rows
  525. * @param {Grid} grid grid object
  526. */
  527. getSelectedRows: function (grid) {
  528. return grid.rows.filter(function (row) {
  529. return row.isSelected;
  530. });
  531. },
  532. /**
  533. * @ngdoc function
  534. * @name clearSelectedRows
  535. * @methodOf ui.grid.selection.service:uiGridSelectionService
  536. * @description Clears all selected rows
  537. * @param {Grid} grid grid object
  538. * @param {Event} evt object if raised from an event
  539. */
  540. clearSelectedRows: function (grid, evt) {
  541. var changedRows = [];
  542. service.getSelectedRows(grid).forEach(function (row) {
  543. if (row.isSelected) {
  544. row.setSelected(false);
  545. service.decideRaiseSelectionEvent(grid, row, changedRows, evt);
  546. }
  547. });
  548. service.decideRaiseSelectionBatchEvent(grid, changedRows, evt);
  549. grid.selection.selectAll = false;
  550. grid.selection.selectedCount = 0;
  551. },
  552. /**
  553. * @ngdoc function
  554. * @name decideRaiseSelectionEvent
  555. * @methodOf ui.grid.selection.service:uiGridSelectionService
  556. * @description Decides whether to raise a single event or a batch event
  557. * @param {Grid} grid grid object
  558. * @param {GridRow} row row that has changed
  559. * @param {array} changedRows an array to which we can append the changed
  560. * @param {Event} evt object if raised from an event
  561. * row if we're doing batch events
  562. */
  563. decideRaiseSelectionEvent: function (grid, row, changedRows, evt) {
  564. if (!grid.options.enableSelectionBatchEvent) {
  565. grid.api.selection.raise.rowSelectionChanged(row, evt);
  566. } else {
  567. changedRows.push(row);
  568. }
  569. },
  570. /**
  571. * @ngdoc function
  572. * @name raiseSelectionEvent
  573. * @methodOf ui.grid.selection.service:uiGridSelectionService
  574. * @description Decides whether we need to raise a batch event, and
  575. * raises it if we do.
  576. * @param {Grid} grid grid object
  577. * @param {array} changedRows an array of changed rows, only populated
  578. * @param {Event} evt object if raised from an event
  579. * if we're doing batch events
  580. */
  581. decideRaiseSelectionBatchEvent: function (grid, changedRows, evt) {
  582. if (changedRows.length > 0) {
  583. grid.api.selection.raise.rowSelectionChangedBatch(changedRows, evt);
  584. }
  585. }
  586. };
  587. return service;
  588. }]);
  589. /**
  590. * @ngdoc directive
  591. * @name ui.grid.selection.directive:uiGridSelection
  592. * @element div
  593. * @restrict A
  594. *
  595. * @description Adds selection features to grid
  596. *
  597. * @example
  598. <example module="app">
  599. <file name="app.js">
  600. var app = angular.module('app', ['ui.grid', 'ui.grid.selection']);
  601. app.controller('MainCtrl', ['$scope', function ($scope) {
  602. $scope.data = [
  603. { name: 'Bob', title: 'CEO' },
  604. { name: 'Frank', title: 'Lowly Developer' }
  605. ];
  606. $scope.columnDefs = [
  607. {name: 'name', enableCellEdit: true},
  608. {name: 'title', enableCellEdit: true}
  609. ];
  610. }]);
  611. </file>
  612. <file name="index.html">
  613. <div ng-controller="MainCtrl">
  614. <div ui-grid="{ data: data, columnDefs: columnDefs }" ui-grid-selection></div>
  615. </div>
  616. </file>
  617. </example>
  618. */
  619. module.directive('uiGridSelection', ['uiGridSelectionConstants', 'uiGridSelectionService', '$templateCache', 'uiGridConstants',
  620. function (uiGridSelectionConstants, uiGridSelectionService, $templateCache, uiGridConstants) {
  621. return {
  622. replace: true,
  623. priority: 0,
  624. require: '^uiGrid',
  625. scope: false,
  626. compile: function () {
  627. return {
  628. pre: function ($scope, $elm, $attrs, uiGridCtrl) {
  629. uiGridSelectionService.initializeGrid(uiGridCtrl.grid);
  630. if (uiGridCtrl.grid.options.enableRowHeaderSelection) {
  631. var selectionRowHeaderDef = {
  632. name: uiGridSelectionConstants.selectionRowHeaderColName,
  633. displayName: '',
  634. width: uiGridCtrl.grid.options.selectionRowHeaderWidth,
  635. minWidth: 10,
  636. cellTemplate: 'ui-grid/selectionRowHeader',
  637. headerCellTemplate: 'ui-grid/selectionHeaderCell',
  638. enableColumnResizing: false,
  639. enableColumnMenu: false,
  640. exporterSuppressExport: true,
  641. allowCellFocus: true
  642. };
  643. uiGridCtrl.grid.addRowHeaderColumn(selectionRowHeaderDef, 0);
  644. }
  645. var processorSet = false;
  646. var processSelectableRows = function (rows) {
  647. rows.forEach(function (row) {
  648. row.enableSelection = uiGridCtrl.grid.options.isRowSelectable(row);
  649. });
  650. return rows;
  651. };
  652. var updateOptions = function () {
  653. if (uiGridCtrl.grid.options.isRowSelectable !== angular.noop && processorSet !== true) {
  654. uiGridCtrl.grid.registerRowsProcessor(processSelectableRows, 500);
  655. processorSet = true;
  656. }
  657. };
  658. updateOptions();
  659. var dataChangeDereg = uiGridCtrl.grid.registerDataChangeCallback(updateOptions, [uiGridConstants.dataChange.OPTIONS]);
  660. $scope.$on('$destroy', dataChangeDereg);
  661. },
  662. post: function ($scope, $elm, $attrs, uiGridCtrl) {
  663. }
  664. };
  665. }
  666. };
  667. }]);
  668. module.directive('uiGridSelectionRowHeaderButtons', ['$templateCache', 'uiGridSelectionService', 'gridUtil',
  669. function ($templateCache, uiGridSelectionService, gridUtil) {
  670. return {
  671. replace: true,
  672. restrict: 'E',
  673. template: $templateCache.get('ui-grid/selectionRowHeaderButtons'),
  674. scope: true,
  675. require: '^uiGrid',
  676. link: function ($scope, $elm, $attrs, uiGridCtrl) {
  677. var self = uiGridCtrl.grid;
  678. $scope.selectButtonClick = selectButtonClick;
  679. $scope.selectButtonKeyDown = selectButtonKeyDown;
  680. // On IE, prevent mousedowns on the select button from starting a selection.
  681. // If this is not done and you shift+click on another row, the browser will select a big chunk of text
  682. if (gridUtil.detectBrowser() === 'ie') {
  683. $elm.on('mousedown', selectButtonMouseDown);
  684. }
  685. function selectButtonKeyDown(row, evt) {
  686. if (evt.keyCode === 32) {
  687. evt.preventDefault();
  688. selectButtonClick(row, evt);
  689. }
  690. }
  691. function selectButtonClick(row, evt) {
  692. evt.stopPropagation();
  693. if (evt.shiftKey) {
  694. uiGridSelectionService.shiftSelect(self, row, evt, self.options.multiSelect);
  695. }
  696. else if (evt.ctrlKey || evt.metaKey) {
  697. uiGridSelectionService.toggleRowSelection(self, row, evt, self.options.multiSelect, self.options.noUnselect);
  698. }
  699. else if (row.groupHeader) {
  700. for (var i = 0; i < row.treeNode.children.length; i++) {
  701. uiGridSelectionService.toggleRowSelection(self, row.treeNode.children[i].row, evt, self.options.multiSelect, self.options.noUnselect);
  702. }
  703. }
  704. else {
  705. uiGridSelectionService.toggleRowSelection(self, row, evt, (self.options.multiSelect && !self.options.modifierKeysToMultiSelect), self.options.noUnselect);
  706. }
  707. }
  708. function selectButtonMouseDown(evt) {
  709. if (evt.ctrlKey || evt.shiftKey) {
  710. evt.target.onselectstart = function () { return false; };
  711. window.setTimeout(function () { evt.target.onselectstart = null; }, 0);
  712. }
  713. }
  714. $scope.$on('$destroy', function unbindEvents() {
  715. $elm.off();
  716. });
  717. }
  718. };
  719. }]);
  720. module.directive('uiGridSelectionSelectAllButtons', ['$templateCache', 'uiGridSelectionService',
  721. function ($templateCache, uiGridSelectionService) {
  722. return {
  723. replace: true,
  724. restrict: 'E',
  725. template: $templateCache.get('ui-grid/selectionSelectAllButtons'),
  726. scope: false,
  727. link: function ($scope, $elm, $attrs, uiGridCtrl) {
  728. var self = $scope.col.grid;
  729. $scope.headerButtonKeyDown = function (evt) {
  730. if (evt.keyCode === 32 || evt.keyCode === 13) {
  731. evt.preventDefault();
  732. $scope.headerButtonClick(evt);
  733. }
  734. };
  735. $scope.headerButtonClick = function (evt) {
  736. if (self.selection.selectAll) {
  737. uiGridSelectionService.clearSelectedRows(self, evt);
  738. if (self.options.noUnselect) {
  739. self.api.selection.selectRowByVisibleIndex(0, evt);
  740. }
  741. self.selection.selectAll = false;
  742. } else if (self.options.multiSelect) {
  743. self.api.selection.selectAllVisibleRows(evt);
  744. self.selection.selectAll = true;
  745. }
  746. };
  747. }
  748. };
  749. }]);
  750. /**
  751. * @ngdoc directive
  752. * @name ui.grid.selection.directive:uiGridViewport
  753. * @element div
  754. *
  755. * @description Stacks on top of ui.grid.uiGridViewport to alter the attributes used
  756. * for the grid row
  757. */
  758. module.directive('uiGridViewport',
  759. ['$compile', 'uiGridConstants', 'uiGridSelectionConstants', 'gridUtil', '$parse', 'uiGridSelectionService',
  760. function ($compile, uiGridConstants, uiGridSelectionConstants, gridUtil, $parse, uiGridSelectionService) {
  761. return {
  762. priority: -200, // run after default directive
  763. scope: false,
  764. compile: function ($elm, $attrs) {
  765. var rowRepeatDiv = angular.element($elm[0].querySelector('.ui-grid-canvas:not(.ui-grid-empty-base-layer-container)').children[0]);
  766. var existingNgClass = rowRepeatDiv.attr("ng-class");
  767. var newNgClass = '';
  768. if (existingNgClass) {
  769. newNgClass = existingNgClass.slice(0, -1) + ",'ui-grid-row-selected': row.isSelected}";
  770. } else {
  771. newNgClass = "{'ui-grid-row-selected': row.isSelected}";
  772. }
  773. rowRepeatDiv.attr("ng-class", newNgClass);
  774. return {
  775. pre: function ($scope, $elm, $attrs, controllers) {
  776. },
  777. post: function ($scope, $elm, $attrs, controllers) {
  778. }
  779. };
  780. }
  781. };
  782. }]);
  783. /**
  784. * @ngdoc directive
  785. * @name ui.grid.selection.directive:uiGridCell
  786. * @element div
  787. * @restrict A
  788. *
  789. * @description Stacks on top of ui.grid.uiGridCell to provide selection feature
  790. */
  791. module.directive('uiGridCell',
  792. ['$compile', 'uiGridConstants', 'uiGridSelectionConstants', 'gridUtil', '$parse', 'uiGridSelectionService', '$timeout',
  793. function ($compile, uiGridConstants, uiGridSelectionConstants, gridUtil, $parse, uiGridSelectionService, $timeout) {
  794. return {
  795. priority: -200, // run after default uiGridCell directive
  796. restrict: 'A',
  797. require: '?^uiGrid',
  798. scope: false,
  799. link: function ($scope, $elm, $attrs, uiGridCtrl) {
  800. var touchStartTime = 0;
  801. var touchTimeout = 300;
  802. // Bind to keydown events in the render container
  803. if (uiGridCtrl.grid.api.cellNav) {
  804. uiGridCtrl.grid.api.cellNav.on.viewPortKeyDown($scope, function (evt, rowCol) {
  805. if (rowCol === null ||
  806. rowCol.row !== $scope.row ||
  807. rowCol.col !== $scope.col) {
  808. return;
  809. }
  810. if (evt.keyCode === 32 && $scope.col.colDef.name === "selectionRowHeaderCol") {
  811. evt.preventDefault();
  812. uiGridSelectionService.toggleRowSelection($scope.grid, $scope.row, evt, ($scope.grid.options.multiSelect && !$scope.grid.options.modifierKeysToMultiSelect), $scope.grid.options.noUnselect);
  813. $scope.$apply();
  814. }
  815. // uiGridCellNavService.scrollToIfNecessary(uiGridCtrl.grid, rowCol.row, rowCol.col);
  816. });
  817. }
  818. //$elm.bind('keydown', function (evt) {
  819. // if (evt.keyCode === 32 && $scope.col.colDef.name === "selectionRowHeaderCol") {
  820. // uiGridSelectionService.toggleRowSelection($scope.grid, $scope.row, evt, ($scope.grid.options.multiSelect && !$scope.grid.options.modifierKeysToMultiSelect), $scope.grid.options.noUnselect);
  821. // $scope.$apply();
  822. // }
  823. //});
  824. var selectCells = function (evt) {
  825. // if you click on expandable icon doesn't trigger selection
  826. if (evt.target.className === "ui-grid-icon-minus-squared" || evt.target.className === "ui-grid-icon-plus-squared") {
  827. return;
  828. }
  829. // if we get a click, then stop listening for touchend
  830. $elm.off('touchend', touchEnd);
  831. if (evt.shiftKey) {
  832. uiGridSelectionService.shiftSelect($scope.grid, $scope.row, evt, $scope.grid.options.multiSelect);
  833. }
  834. else if (evt.ctrlKey || evt.metaKey) {
  835. uiGridSelectionService.toggleRowSelection($scope.grid, $scope.row, evt, $scope.grid.options.multiSelect, $scope.grid.options.noUnselect);
  836. }
  837. else {
  838. uiGridSelectionService.toggleRowSelection($scope.grid, $scope.row, evt, ($scope.grid.options.multiSelect && !$scope.grid.options.modifierKeysToMultiSelect), $scope.grid.options.noUnselect);
  839. }
  840. $scope.$apply();
  841. // don't re-enable the touchend handler for a little while - some devices generate both, and it will
  842. // take a little while to move your hand from the mouse to the screen if you have both modes of input
  843. $timeout(function () {
  844. $elm.on('touchend', touchEnd);
  845. }, touchTimeout);
  846. };
  847. var touchStart = function (evt) {
  848. touchStartTime = (new Date()).getTime();
  849. // if we get a touch event, then stop listening for click
  850. $elm.off('click', selectCells);
  851. };
  852. var touchEnd = function (evt) {
  853. var touchEndTime = (new Date()).getTime();
  854. var touchTime = touchEndTime - touchStartTime;
  855. if (touchTime < touchTimeout) {
  856. // short touch
  857. selectCells(evt);
  858. }
  859. // don't re-enable the click handler for a little while - some devices generate both, and it will
  860. // take a little while to move your hand from the screen to the mouse if you have both modes of input
  861. $timeout(function () {
  862. $elm.on('click', selectCells);
  863. }, touchTimeout);
  864. };
  865. function registerRowSelectionEvents() {
  866. if ($scope.grid.options.enableRowSelection && $scope.grid.options.enableFullRowSelection && !$elm.hasClass('ui-grid-row-header-cell')) {
  867. $elm.addClass('ui-grid-disable-selection');
  868. $elm.on('touchstart', touchStart);
  869. $elm.on('touchend', touchEnd);
  870. $elm.on('click', selectCells);
  871. $scope.registered = true;
  872. }
  873. }
  874. function deregisterRowSelectionEvents() {
  875. if ($scope.registered) {
  876. $elm.removeClass('ui-grid-disable-selection');
  877. $elm.off('touchstart', touchStart);
  878. $elm.off('touchend', touchEnd);
  879. $elm.off('click', selectCells);
  880. $scope.registered = false;
  881. }
  882. }
  883. registerRowSelectionEvents();
  884. // register a dataChange callback so that we can change the selection configuration dynamically
  885. // if the user changes the options
  886. var dataChangeDereg = $scope.grid.registerDataChangeCallback(function () {
  887. if ($scope.grid.options.enableRowSelection && $scope.grid.options.enableFullRowSelection &&
  888. !$scope.registered) {
  889. registerRowSelectionEvents();
  890. } else if ((!$scope.grid.options.enableRowSelection || !$scope.grid.options.enableFullRowSelection) &&
  891. $scope.registered) {
  892. deregisterRowSelectionEvents();
  893. }
  894. }, [uiGridConstants.dataChange.OPTIONS]);
  895. $elm.on('$destroy', dataChangeDereg);
  896. }
  897. };
  898. }]);
  899. module.directive('uiGridGridFooter', ['$compile', 'uiGridConstants', 'gridUtil', function ($compile, uiGridConstants, gridUtil) {
  900. return {
  901. restrict: 'EA',
  902. replace: true,
  903. priority: -1000,
  904. require: '^uiGrid',
  905. scope: true,
  906. compile: function ($elm, $attrs) {
  907. return {
  908. pre: function ($scope, $elm, $attrs, uiGridCtrl) {
  909. if (!uiGridCtrl.grid.options.showGridFooter) {
  910. return;
  911. }
  912. gridUtil.getTemplate('ui-grid/gridFooterSelectedItems')
  913. .then(function (contents) {
  914. var template = angular.element(contents);
  915. var newElm = $compile(template)($scope);
  916. angular.element($elm[0].getElementsByClassName('ui-grid-grid-footer')[0]).append(newElm);
  917. });
  918. },
  919. post: function ($scope, $elm, $attrs, controllers) {
  920. }
  921. };
  922. }
  923. };
  924. }]);
  925. })();