multi-select-tree.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*jshint indent: 2 */
  2. /*global angular: false */
  3. (function () {
  4. 'use strict';
  5. var mainModule = angular.module('multi-select-tree');
  6. /**
  7. * Controller for multi select tree.
  8. */
  9. mainModule.controller('multiSelectTreeCtrl', ['$scope', '$document', function ($scope, $document) {
  10. var activeItem;
  11. console.log($document)
  12. $scope.showTree = false;
  13. $scope.selectedItems = [];
  14. $scope.multiSelect = $scope.multiSelect || false;
  15. /**
  16. * Clicking on document will hide the tree.
  17. */
  18. function docClickHide() {
  19. closePopup();
  20. $scope.$apply();
  21. }
  22. /**
  23. * Closes the tree popup.
  24. */
  25. function closePopup() {
  26. $scope.showTree = false;
  27. if (activeItem) {
  28. activeItem.isActive = false;
  29. activeItem = undefined;
  30. }
  31. $document.off('click', docClickHide);
  32. }
  33. /**
  34. * Sets the active item.
  35. *
  36. * @param item the item element.
  37. */
  38. $scope.onActiveItem = function (item) {
  39. if (activeItem !== item) {
  40. if (activeItem) {
  41. activeItem.isActive = false;
  42. }
  43. activeItem = item;
  44. activeItem.isActive = true;
  45. }
  46. };
  47. /**
  48. * Copies the selectedItems in to output model.
  49. */
  50. $scope.refreshOutputModel = function () {
  51. $scope.outputModel = angular.copy($scope.selectedItems);
  52. };
  53. /**
  54. * Refreshes the selected Items model.
  55. */
  56. $scope.refreshSelectedItems = function () {
  57. $scope.selectedItems = [];
  58. if ($scope.inputModel) {
  59. setSelectedChildren($scope.inputModel);
  60. }
  61. };
  62. /**
  63. * Iterates over children and sets the selected items.
  64. *
  65. * @param children the children element.
  66. */
  67. function setSelectedChildren(children) {
  68. for (var i = 0, len = children.length; i < len; i++) {
  69. if (!isItemSelected(children[i]) && children[i].selected === true) {
  70. $scope.selectedItems.push(children[i]);
  71. } else if (isItemSelected(children[i]) && children[i].selected === false) {
  72. children[i].selected = true;
  73. }
  74. if (children[i] && children[i].children) {
  75. setSelectedChildren(children[i].children);
  76. }
  77. }
  78. }
  79. /**
  80. * Checks of the item is already selected.
  81. *
  82. * @param item the item to be checked.
  83. * @return {boolean} if the item is already selected.
  84. */
  85. function isItemSelected(item) {
  86. var isSelected = false;
  87. if ($scope.selectedItems) {
  88. for (var i = 0; i < $scope.selectedItems.length; i++) {
  89. if ($scope.selectedItems[i].id === item.id) {
  90. isSelected = true;
  91. break;
  92. }
  93. }
  94. }
  95. return isSelected;
  96. }
  97. /**
  98. * Deselect the item.
  99. *
  100. * @param item the item element
  101. * @param $event
  102. */
  103. $scope.deselectItem = function (item, $event) {
  104. $event.stopPropagation();
  105. $scope.selectedItems.splice($scope.selectedItems.indexOf(item), 1);
  106. item.selected = false;
  107. this.refreshOutputModel();
  108. };
  109. /**
  110. * Swap the tree popup on control click event.
  111. *
  112. * @param $event the click event.
  113. */
  114. $scope.onControlClicked = function ($event) {
  115. $event.stopPropagation();
  116. $scope.showTree = !$scope.showTree;
  117. if ($scope.showTree) {
  118. $document.on('click', docClickHide);
  119. }
  120. };
  121. /**
  122. * Stop the event on filter clicked.
  123. *
  124. * @param $event the click event
  125. */
  126. $scope.onFilterClicked = function ($event) {
  127. $event.stopPropagation();
  128. };
  129. /**
  130. * Clears the filter text.
  131. *
  132. * @param $event the click event
  133. */
  134. $scope.clearFilter = function ($event) {
  135. $event.stopPropagation();
  136. $scope.filterKeyword = '';
  137. };
  138. /**
  139. * Wrapper function for can select item callback.
  140. *
  141. * @param item the item
  142. */
  143. $scope.canSelectItem = function (item) {
  144. return $scope.callback({item: item, selectedItems: $scope.selectedItems});
  145. };
  146. /**
  147. * The callback is used to switch the views.
  148. * based on the view type.
  149. *
  150. * @param $event the event object.
  151. */
  152. $scope.switchCurrentView = function($event) {
  153. $event.stopPropagation();
  154. $scope.switchViewCallback({scopeObj:$scope});
  155. };
  156. /**
  157. * Handles the item select event.
  158. *
  159. * @param item the selected item.
  160. */
  161. $scope.itemSelected = function (item) {
  162. if (($scope.useCallback && $scope.canSelectItem(item) === false) ||
  163. ($scope.selectOnlyLeafs && item.children && item.children.length > 0)) {
  164. return;
  165. }
  166. if (!$scope.multiSelect) {
  167. closePopup();
  168. for (var i = 0; i < $scope.selectedItems.length; i++) {
  169. $scope.selectedItems[i].selected = false;
  170. }
  171. item.selected = true;
  172. $scope.selectedItems = [];
  173. $scope.selectedItems.push(item);
  174. } else {
  175. item.selected = true;
  176. var indexOfItem = $scope.selectedItems.indexOf(item);
  177. if (isItemSelected(item)) {
  178. item.selected = false;
  179. $scope.selectedItems.splice(indexOfItem, 1);
  180. } else {
  181. $scope.selectedItems.push(item);
  182. }
  183. }
  184. this.refreshOutputModel();
  185. };
  186. }]);
  187. /**
  188. * sortableItem directive.
  189. */
  190. mainModule.directive('multiSelectTree',
  191. function () {
  192. return {
  193. restrict: 'E',
  194. templateUrl: 'src/multi-select-tree.tpl.html',
  195. scope: {
  196. inputModel: '=',
  197. outputModel: '=?',
  198. multiSelect: '=?',
  199. switchView: '=?',
  200. switchViewLabel: '@',
  201. switchViewCallback: '&',
  202. selectOnlyLeafs: '=?',
  203. callback: '&',
  204. defaultLabel: '@'
  205. },
  206. link: function (scope, element, attrs) {
  207. if (attrs.callback) {
  208. scope.useCallback = true;
  209. }
  210. // watch for changes in input model as a whole
  211. // this on updates the multi-select when a user load a whole new input-model.
  212. scope.$watch('inputModel', function (newVal) {
  213. if (newVal) {
  214. scope.refreshSelectedItems();
  215. scope.refreshOutputModel();
  216. }
  217. });
  218. /**
  219. * Checks whether any of children match the keyword.
  220. *
  221. * @param item the parent item
  222. * @param keyword the filter keyword
  223. * @returns {boolean} false if matches.
  224. */
  225. function isChildrenFiltered(item, keyword) {
  226. var childNodes = getAllChildNodesFromNode(item, []);
  227. for (var i = 0, len = childNodes.length; i < len; i++) {
  228. if (childNodes[i].name.toLowerCase().indexOf(keyword.toLowerCase()) !== -1) {
  229. return false;
  230. }
  231. }
  232. return true;
  233. }
  234. /**
  235. * Return all childNodes of a given node (as Array of Nodes)
  236. */
  237. function getAllChildNodesFromNode(node, childNodes) {
  238. for (var i = 0; i < node.children.length; i++) {
  239. childNodes.push(node.children[i]);
  240. // add the childNodes from the children if available
  241. getAllChildNodesFromNode(node.children[i], childNodes);
  242. }
  243. return childNodes;
  244. }
  245. scope.$watch('filterKeyword', function () {
  246. if (scope.filterKeyword !== undefined) {
  247. angular.forEach(scope.inputModel, function (item) {
  248. if (item.name.toLowerCase().indexOf(scope.filterKeyword.toLowerCase()) !== -1) {
  249. item.isFiltered = false;
  250. } else if (!isChildrenFiltered(item, scope.filterKeyword)) {
  251. item.isFiltered = false;
  252. } else {
  253. item.isFiltered = true;
  254. }
  255. });
  256. }
  257. });
  258. },
  259. controller: 'multiSelectTreeCtrl'
  260. };
  261. });
  262. }());