@@ -14,12 +14,15 @@ var SelectController =
14
14
[ '$element' , '$scope' , function ( $element , $scope ) {
15
15
16
16
var self = this ,
17
- optionsMap = new HashMap ( ) ;
17
+ optionsMap = new HashMap ( ) ,
18
+ handleMultipleDestroy = false ; // Flag to run an update to the model after selected options
19
+ // in a multiple select have been destroyed
18
20
19
21
self . selectValueMap = { } ; // Keys are the hashed values, values the original values
20
22
21
23
// If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
22
24
self . ngModelCtrl = noopNgModelController ;
25
+ self . multiple = false ;
23
26
24
27
// The "unknown" option is one that is prepended to the list if the viewValue
25
28
// does not match any of the options. When it is rendered the value of the unknown
@@ -52,10 +55,7 @@ var SelectController =
52
55
// ngValue added option values are stored in the selectValueMap, normal interpolations are not
53
56
var realVal = val in self . selectValueMap ? self . selectValueMap [ val ] : val ;
54
57
55
- console . log ( 'read' , 'elval' , val , 'possiblyhashed' , realVal )
56
58
if ( self . hasOption ( realVal ) ) {
57
- console . log ( 'has selected val' , realVal )
58
- // self.removeUnknownOption();
59
59
return realVal ;
60
60
}
61
61
@@ -66,25 +66,15 @@ var SelectController =
66
66
// Write the value to the select control, the implementation of this changes depending
67
67
// upon whether the select can have multiple values and whether ngOptions is at work.
68
68
self . writeValue = function writeSingleValue ( value ) {
69
- console . log ( 'write' , value ) ;
70
69
if ( self . hasOption ( value ) ) {
71
- console . log ( 'hasOption' , value ) ;
72
70
self . removeUnknownOption ( ) ;
73
71
var hashedVal = hashKey ( value ) ;
74
72
if ( hashedVal in self . selectValueMap ) {
75
73
$element . val ( hashedVal ) ;
76
74
} else {
77
75
$element . val ( value ) ;
78
76
}
79
- console . log ( 'after set' , $element . val ( ) )
80
- // console.log('selectValueMap', self.selectValueMap)
81
- // var items = new HashMap();
82
- // items.put(value, value);
83
- // console.log(items, hashKey(value));
84
-
85
- // if (hashKey(value) in self.selectValueMap) {
86
- // console.log('hashed')
87
- // }
77
+
88
78
if ( value === '' ) self . emptyOption . prop ( 'selected' , true ) ; // to make IE9 happy
89
79
} else {
90
80
if ( value == null && self . emptyOption ) {
@@ -131,18 +121,32 @@ var SelectController =
131
121
return ! ! optionsMap . get ( value ) ;
132
122
} ;
133
123
124
+ var handleMultipleChanges = false ;
125
+ function updateModelAfterOptionChange ( renderAfter ) {
126
+ if ( self . multiple ) {
127
+ if ( ! handleMultipleChanges ) {
128
+ handleMultipleChanges = true ;
129
+ } else {
130
+ $scope . $$postDigest ( function ( ) {
131
+ handleMultipleChanges = false ;
132
+ self . ngModelCtrl . $setViewValue ( self . readValue ( ) ) ;
133
+ if ( renderAfter ) self . ngModelCtrl . $render ( ) ;
134
+ } ) ;
135
+ }
136
+ } else {
137
+ self . ngModelCtrl . $setViewValue ( self . readValue ( ) ) ;
138
+ }
139
+ }
140
+
134
141
135
142
self . registerOption = function ( optionScope , optionElement , optionAttrs , interpolateValueFn , interpolateTextFn ) {
136
143
137
- // console.log('attr', optionAttrs)
138
144
if ( optionAttrs . $attr . ngValue ) {
139
145
// The value attribute is set by ngValue
140
146
var oldVal , hashedVal = NaN ;
141
147
optionAttrs . $observe ( 'value' , function valueAttributeObserveAction ( newVal ) {
142
- console . log ( 'ngValue change' , 'n' , newVal , 'o' , oldVal , 'hashed' , hashedVal )
143
148
144
149
var removal ;
145
- console . log ( 'val' , $element . val ( ) ) ;
146
150
var previouslySelected = optionElement . prop ( 'selected' ) ;
147
151
148
152
if ( isDefined ( hashedVal ) ) {
@@ -155,24 +159,19 @@ var SelectController =
155
159
oldVal = newVal ;
156
160
self . selectValueMap [ hashedVal ] = newVal ;
157
161
self . addOption ( newVal , optionElement ) ;
158
- console . log ( 'val' , $element . val ( ) ) ;
159
162
// Set the attribute directly instead of using optionAttrs.$set - this stops the observer
160
163
// from firing a second time. Other $observers on value will also get the result of the
161
164
// ngValue expression, not the hashed value
162
165
optionElement . attr ( 'value' , hashedVal ) ;
163
166
164
- console . log ( 'previouslySelected' , previouslySelected , 'removal' , removal )
165
-
166
167
if ( removal && previouslySelected ) {
167
- console . log ( 'removed val is currently selected' , $element . val ( ) )
168
- self . ngModelCtrl . $setViewValue ( self . readValue ( ) ) ;
169
- }
168
+ updateModelAfterOptionChange ( ) ;
169
+ }
170
170
171
171
} ) ;
172
172
} else if ( interpolateValueFn ) {
173
173
// The value attribute is interpolated
174
174
optionAttrs . $observe ( 'value' , function valueAttributeObserveAction ( newVal ) {
175
- // console.log('value attribute changed', 'viewVal', self.ngModelCtrl.$viewValue, 'index', optionElement[0].index, 'indices', $element[0].selectedIndex, $element[0].selectedOptions)
176
175
var currentVal = self . readValue ( ) ;
177
176
var removal ;
178
177
var previouslySelected = optionElement . prop ( 'selected' ) ;
@@ -186,11 +185,9 @@ var SelectController =
186
185
oldVal = newVal ;
187
186
self . addOption ( newVal , optionElement ) ;
188
187
189
- console . log ( 'updated interpolated value' , 'new' , newVal , 'removed' , removedVal , 'current' , currentVal ) ;
190
188
if ( removal && previouslySelected ) {
191
- console . log ( 'removed val is currently selected' , $element . val ( ) )
192
- self . ngModelCtrl . $setViewValue ( self . readValue ( ) ) ;
193
- }
189
+ updateModelAfterOptionChange ( ) ;
190
+ }
194
191
} ) ;
195
192
} else if ( interpolateTextFn ) {
196
193
// The text content is interpolated
@@ -203,7 +200,7 @@ var SelectController =
203
200
self . addOption ( newVal , optionElement ) ;
204
201
205
202
if ( oldVal && previouslySelected ) {
206
- self . ngModelCtrl . $setViewValue ( self . readValue ( ) ) ;
203
+ updateModelAfterOptionChange ( ) ;
207
204
}
208
205
} ) ;
209
206
} else {
@@ -219,39 +216,33 @@ var SelectController =
219
216
// we only have to handle options becomeing disabled, not enabled
220
217
221
218
if ( newVal === 'true' || newVal && optionElement . prop ( 'selected' ) ) {
222
- console . log ( 'disabled' )
223
- self . ngModelCtrl . $setViewValue ( null ) ;
224
- self . ngModelCtrl . $render ( ) ;
219
+
220
+ if ( self . multiple ) {
221
+ updateModelAfterOptionChange ( true ) ;
222
+ } else {
223
+ self . ngModelCtrl . $setViewValue ( null ) ;
224
+ self . ngModelCtrl . $render ( ) ;
225
+ }
225
226
oldDisabled = newVal ;
226
- } else if ( isDefined ( oldDisabled ) && ! newVal || newVal === 'false' ) {
227
- // var val = optionAttrs.value;
228
- // console.log('OA', optionAttrs.value);
229
- // var realVal = val in self.selectValueMap ? self.selectValueMap[val] : val;
230
- // console.log('back from disabled', val, realVal, self.ngModelCtrl.$viewValue);
231
-
232
- // if (realVal === self.ngModelCtrl.$viewValue) {
233
- // self.writeValue(realVal);
234
- // self.ngModelCtrl.$setViewValue(self.readValue());
235
- // }
236
227
}
237
228
} ) ;
238
229
239
230
optionElement . on ( '$destroy' , function ( ) {
240
231
var currentValue = self . readValue ( ) ;
241
232
var removeValue = optionAttrs . value ;
242
233
243
- console . log ( 'destroy' , removeValue , 'elval' , $element . val ( ) )
244
- // console.log('viewValue', self.ngModelCtrl.$viewValue)
245
234
self . removeOption ( removeValue ) ;
246
235
self . ngModelCtrl . $render ( ) ;
247
236
248
- if ( currentValue === removeValue ) {
249
- // console.log('removed val is currently selected', $element.val())
250
- // console.log('self.readValue()', self.readValue());
237
+ if ( self . multiple && currentValue && currentValue . indexOf ( removeValue ) !== - 1 ) {
238
+ // When multiple (selected) options are destroyed at the same time, we don't want
239
+ // to run a model update for each of them. Instead, run a single update in the $$postDigest
240
+ // NOTE: Will that interfere with the regular model update? No - $setViewValue always triggers a digest
241
+ // However, it might not work if someones removes an option outside of a digest
242
+ updateModelAfterOptionChange ( ) ;
243
+ } else if ( currentValue === removeValue ) {
251
244
self . ngModelCtrl . $setViewValue ( self . readValue ( ) ) ;
252
-
253
245
}
254
- // console.log('read after render', self.readValue())
255
246
} ) ;
256
247
} ;
257
248
} ] ;
@@ -487,7 +478,6 @@ var selectDirective = function() {
487
478
// to the `readValue` method, which can be changed if the select can have multiple
488
479
// selected values or if the options are being generated by `ngOptions`
489
480
element . on ( 'change' , function ( ) {
490
- console . log ( 'on change' , element . val ( ) )
491
481
selectCtrl . removeUnknownOption ( ) ;
492
482
scope . $apply ( function ( ) {
493
483
ngModelCtrl . $setViewValue ( selectCtrl . readValue ( ) ) ;
@@ -499,12 +489,13 @@ var selectDirective = function() {
499
489
// we have to add an extra watch since ngModel doesn't work well with arrays - it
500
490
// doesn't trigger rendering if only an item in the array changes.
501
491
if ( attr . multiple ) {
492
+ selectCtrl . multiple = true ;
502
493
503
494
// Read value now needs to check each option to see if it is selected
504
495
selectCtrl . readValue = function readMultipleValue ( ) {
505
496
var array = [ ] ;
506
497
forEach ( element . find ( 'option' ) , function ( option ) {
507
- if ( option . selected ) {
498
+ if ( option . selected && ! option . disabled ) {
508
499
var val = option . value ;
509
500
array . push ( val in selectCtrl . selectValueMap ? selectCtrl . selectValueMap [ val ] : val ) ;
510
501
}
@@ -570,6 +561,7 @@ var optionDirective = ['$interpolate', function($interpolate) {
570
561
var interpolateValueFn , interpolateTextFn ;
571
562
572
563
if ( isDefined ( attr . ngValue ) ) {
564
+ // jshint noempty: false
573
565
// Will be handled by registerOption
574
566
} else if ( isDefined ( attr . value ) ) {
575
567
// If the value attribute is defined, check if it contains an interpolation
0 commit comments