1313 } , object )
1414 } ;
1515
16- multiselect . directive ( 'multiselect' , [ '$filter' , '$document' , function ( $filter , $document ) {
16+ multiselect . directive ( 'multiselect' , [ '$filter' , '$document' , '$log' , function ( $filter , $document , $log ) {
1717 return {
1818 restrict : 'AE' ,
1919 scope : {
2424 selectionLimit : '=?' ,
2525 showSelectAll : '=?' ,
2626 showUnselectAll : '=?' ,
27- showSearch : '=?'
27+ showSearch : '=?' ,
28+ searchFilter : '=?' ,
29+ disabled : '=?ngDisabled'
2830 } ,
2931 require : 'ngModel' ,
3032 templateUrl : 'multiselect.html' ,
3436
3537 $scope . searchFilter = '' ;
3638
39+ if ( typeof $scope . options !== 'function' ) {
40+ $scope . resolvedOptions = $scope . options ;
41+ }
42+
43+ if ( typeof $attrs . disabled != 'undefined' ) {
44+ $scope . disabled = true ;
45+ }
46+
3747 $scope . toggleDropdown = function ( ) {
3848 $scope . open = ! $scope . open ;
3949 } ;
4858
4959 $document . on ( 'click' , closeHandler ) ;
5060
61+ var updateSelectionLists = function ( ) {
62+ if ( ! $ngModelCtrl . $viewValue ) {
63+ if ( $scope . selectedOptions ) {
64+ $scope . selectedOptions = [ ] ;
65+ }
66+ $scope . unselectedOptions = angular . copy ( $scope . resolvedOptions ) ;
67+ } else {
68+ $scope . selectedOptions = $scope . resolvedOptions . filter ( function ( el ) {
69+ var id = $scope . getId ( el ) ;
70+ for ( var i = 0 ; i < $ngModelCtrl . $viewValue . length ; i ++ ) {
71+ var selectedId = $scope . getId ( $ngModelCtrl . $viewValue [ i ] ) ;
72+ if ( id === selectedId ) {
73+ return true ;
74+ }
75+ }
76+ return false ;
77+ } ) ;
78+ $scope . unselectedOptions = $scope . resolvedOptions . filter ( function ( el ) {
79+ return $scope . selectedOptions . indexOf ( el ) < 0 ;
80+ } ) ;
81+ }
82+ } ;
83+
5184 $ngModelCtrl . $render = function ( ) {
52- $scope . selection = $ngModelCtrl . $viewValue ;
85+ updateSelectionLists ( ) ;
5386 } ;
5487
5588 $ngModelCtrl . $viewChangeListeners . push ( function ( ) {
56- $scope . selection = $ngModelCtrl . $viewValue ;
89+ updateSelectionLists ( ) ;
5790 } ) ;
5891
5992 $ngModelCtrl . $isEmpty = function ( value ) {
6497 }
6598 } ;
6699
67- var watcher = $scope . $watch ( 'selection ' , function ( ) {
68- $ngModelCtrl . $setViewValue ( angular . copy ( $scope . selection ) ) ;
100+ var watcher = $scope . $watch ( 'selectedOptions ' , function ( ) {
101+ $ngModelCtrl . $setViewValue ( angular . copy ( $scope . selectedOptions ) ) ;
69102 } , true ) ;
70103
71104 $scope . $on ( '$destroy' , function ( ) {
76109 } ) ;
77110
78111 $scope . getButtonText = function ( ) {
79- if ( $scope . selection && $scope . selection . length === 1 ) {
80- return $scope . getDisplay ( $scope . selection [ 0 ] ) ;
112+ if ( $scope . selectedOptions && $scope . selectedOptions . length === 1 ) {
113+ return $scope . getDisplay ( $scope . selectedOptions [ 0 ] ) ;
81114 }
82- if ( $scope . selection && $scope . selection . length > 1 ) {
115+ if ( $scope . selectedOptions && $scope . selectedOptions . length > 1 ) {
83116 var totalSelected ;
84- totalSelected = angular . isDefined ( $scope . selection ) ? $scope . selection . length : 0 ;
117+ totalSelected = angular . isDefined ( $scope . selectedOptions ) ? $scope . selectedOptions . length : 0 ;
85118 if ( totalSelected === 0 ) {
86119 return 'Select' ;
87120 } else {
93126 } ;
94127
95128 $scope . selectAll = function ( ) {
96- $scope . unselectAll ( ) ;
97- angular . forEach ( $scope . options , function ( value ) {
98- $scope . toggleItem ( value ) ;
99- } ) ;
129+ $scope . selectedOptions = $scope . resolvedOptions ;
130+ $scope . unselectedOptions = [ ] ;
100131 } ;
101132
102133 $scope . unselectAll = function ( ) {
103- $scope . selection = [ ] ;
134+ $scope . selectedOptions = [ ] ;
135+ $scope . unselectedOptions = $scope . resolvedOptions ;
104136 } ;
105137
106138 $scope . toggleItem = function ( item ) {
107- if ( typeof $scope . selection === 'undefined' ) {
108- $scope . selection = [ ] ;
139+ if ( typeof $scope . selectedOptions === 'undefined' ) {
140+ $scope . selectedOptions = [ ] ;
109141 }
110- var index = $scope . selection . indexOf ( item ) ;
111- var exists = index !== - 1 ;
112- if ( exists ) {
113- $scope . selection . splice ( index , 1 ) ;
114- } else if ( ! exists && ( $scope . selectionLimit === 0 || $scope . selection . length < $scope . selectionLimit ) ) {
115- if ( ! angular . isDefined ( $scope . selection ) || $scope . selection == null ) {
116- $scope . selection = [ ] ;
117- }
118- $scope . selection . push ( item ) ;
142+ var selectedIndex = $scope . selectedOptions . indexOf ( item ) ;
143+ var currentlySelected = ( selectedIndex !== - 1 ) ;
144+ if ( currentlySelected ) {
145+ $scope . unselectedOptions . push ( $scope . selectedOptions [ selectedIndex ] ) ;
146+ $scope . selectedOptions . splice ( selectedIndex , 1 ) ;
147+ } else if ( ! currentlySelected && ( $scope . selectionLimit === 0 || $scope . selectedOptions . length < $scope . selectionLimit ) ) {
148+ var unselectedIndex = $scope . unselectedOptions . indexOf ( item ) ;
149+ $scope . unselectedOptions . splice ( unselectedIndex , 1 ) ;
150+ $scope . selectedOptions . push ( item ) ;
119151 }
120152 } ;
121153
122154 $scope . getId = function ( item ) {
123155 if ( angular . isString ( item ) ) {
124156 return item ;
125157 } else if ( angular . isObject ( item ) ) {
126- return multiselect . getRecursiveProperty ( item , $scope . idProp ) ;
158+ if ( $scope . idProp ) {
159+ return multiselect . getRecursiveProperty ( item , $scope . idProp ) ;
160+ } else {
161+ $log . error ( 'Multiselect: when using objects as model, a idProp value is mandatory.' ) ;
162+ return '' ;
163+ }
127164 } else {
128165 return item ;
129166 }
133170 if ( angular . isString ( item ) ) {
134171 return item ;
135172 } else if ( angular . isObject ( item ) ) {
136- return multiselect . getRecursiveProperty ( item , $scope . displayProp ) ;
173+ if ( $scope . displayProp ) {
174+ return multiselect . getRecursiveProperty ( item , $scope . displayProp ) ;
175+ } else {
176+ $log . error ( 'Multiselect: when using objects as model, a displayProp value is mandatory.' ) ;
177+ return '' ;
178+ }
137179 } else {
138180 return item ;
139181 }
140182 } ;
141183
142184 $scope . isSelected = function ( item ) {
143- var result = false ;
144- angular . forEach ( $scope . selection , function ( selectedElement ) {
145- if ( $scope . getId ( selectedElement ) === $scope . getId ( item ) ) {
146- result = true ;
185+ if ( ! $scope . selectedOptions ) {
186+ return false ;
187+ }
188+ var itemId = $scope . getId ( item ) ;
189+ for ( var i = 0 ; i < $scope . selectedOptions . length ; i ++ ) {
190+ var selectedElement = $scope . selectedOptions [ i ] ;
191+ if ( $scope . getId ( selectedElement ) === itemId ) {
192+ return true ;
147193 }
148- } ) ;
149- return result ;
194+ }
195+ return false ;
196+ } ;
197+
198+ $scope . updateOptions = function ( ) {
199+ if ( typeof $scope . options === 'function' ) {
200+ $scope . options ( ) . then ( function ( resolvedOptions ) {
201+ $scope . resolvedOptions = resolvedOptions ;
202+ updateSelectionLists ( ) ;
203+ } ) ;
204+ }
150205 } ;
151206
152207 // This search function is optimized to take into account the search limit.
@@ -180,8 +235,9 @@ angular.module('btorfs.multiselect.templates', ['multiselect.html']);
180235angular . module ( "multiselect.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
181236 $templateCache . put ( "multiselect.html" ,
182237 "<div class=\"btn-group\" style=\"width: 100%\">\n" +
183- " <button type=\"button\" class=\"form-control btn btn-default btn-block dropdown-toggle\" ng-click=\"toggleDropdown()\">\n" +
184- " {{getButtonText()}} <span class=\"caret\"></span></button>\n" +
238+ " <button type=\"button\" class=\"form-control btn btn-default btn-block dropdown-toggle\" ng-click=\"toggleDropdown()\" ng-disabled=\"disabled\">\n" +
239+ " {{getButtonText()}} <span class=\"caret\"></span>\n" +
240+ " </button>\n" +
185241 " <ul class=\"dropdown-menu dropdown-menu-form\"\n" +
186242 " ng-style=\"{display: open ? 'block' : 'none'}\" style=\"width: 100%; overflow-x: auto\">\n" +
187243 "\n" +
@@ -199,32 +255,33 @@ angular.module("multiselect.html", []).run(["$templateCache", function($template
199255 " class=\"divider\">\n" +
200256 " </li>\n" +
201257 "\n" +
202- " <li role=\"presentation\" ng-repeat=\"option in selection \" class=\"active\">\n" +
258+ " <li role=\"presentation\" ng-repeat=\"option in selectedOptions \" class=\"active\">\n" +
203259 " <a class=\"item-selected\" href=\"\" ng-click=\"toggleItem(option); $event.stopPropagation()\">\n" +
204260 " <span class=\"glyphicon glyphicon-remove\"></span>\n" +
205261 " {{getDisplay(option)}}\n" +
206262 " </a>\n" +
207263 " </li>\n" +
208- " <li ng-show=\"selection .length > 0\" class=\"divider\"></li>\n" +
264+ " <li ng-show=\"selectedOptions .length > 0\" class=\"divider\"></li>\n" +
209265 "\n" +
210266 " <li ng-show=\"showSearch\">\n" +
211267 " <div class=\"dropdown-header\">\n" +
212268 " <input type=\"text\" class=\"form-control input-sm\" style=\"width: 100%;\"\n" +
213- " ng-model=\"searchFilter\" placeholder=\"Search...\"/>\n" +
269+ " ng-model=\"searchFilter\" placeholder=\"Search...\" ng-change=\"updateOptions()\" />\n" +
214270 " </div>\n" +
215271 " </li>\n" +
216272 "\n" +
217273 " <li ng-show=\"showSearch\" class=\"divider\"></li>\n" +
218- " <li role=\"presentation\" ng-repeat=\"option in options | filter:search() | limitTo: searchLimit\"\n" +
219- " ng-if=\"!isSelected(option)\">\n" +
274+ " <li role=\"presentation\" ng-repeat=\"option in unselectedOptions | filter:search() | limitTo: searchLimit\"\n" +
275+ " ng-if=\"!isSelected(option)\"\n" +
276+ " ng-class=\"{disabled : selectionLimit && selectedOptions.length >= selectionLimit}\">\n" +
220277 " <a class=\"item-unselected\" href=\"\" ng-click=\"toggleItem(option); $event.stopPropagation()\">\n" +
221278 " {{getDisplay(option)}}\n" +
222279 " </a>\n" +
223280 " </li>\n" +
224281 "\n" +
225282 " <li class=\"divider\" ng-show=\"selectionLimit > 1\"></li>\n" +
226283 " <li role=\"presentation\" ng-show=\"selectionLimit > 1\">\n" +
227- " <a>{{selection .length || 0}} / {{selectionLimit}} selected</a>\n" +
284+ " <a>{{selectedOptions .length || 0}} / {{selectionLimit}} selected</a>\n" +
228285 " </li>\n" +
229286 "\n" +
230287 " </ul>\n" +
0 commit comments