@@ -20,6 +20,7 @@ var SelectController =
20
20
21
21
// If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
22
22
self . ngModelCtrl = noopNgModelController ;
23
+ self . multiple = false ;
23
24
24
25
// The "unknown" option is one that is prepended to the list if the viewValue
25
26
// does not match any of the options. When it is rendered the value of the unknown
@@ -91,7 +92,9 @@ var SelectController =
91
92
}
92
93
var count = optionsMap . get ( value ) || 0 ;
93
94
optionsMap . put ( value , count + 1 ) ;
94
- self . ngModelCtrl . $render ( ) ;
95
+ // Only render at the end of a digest. This improves render performance when many options
96
+ // are added during a digest and ensures all relevant options are correctly marked as selected
97
+ scheduleRender ( ) ;
95
98
} ;
96
99
97
100
// Tell the select control that an option, with the given value, has been removed
@@ -115,6 +118,15 @@ var SelectController =
115
118
} ;
116
119
117
120
121
+ var renderScheduled = false ;
122
+ function scheduleRender ( ) {
123
+ if ( renderScheduled ) return ;
124
+ renderScheduled = true ;
125
+ $scope . $$postDigest ( function ( ) {
126
+ renderScheduled = false ;
127
+ self . ngModelCtrl . $render ( ) ;
128
+ } ) ;
129
+ }
118
130
119
131
var updateScheduled = false ;
120
132
function scheduleViewValueUpdate ( renderAfter ) {
@@ -163,29 +175,74 @@ var SelectController =
163
175
} else if ( interpolateValueFn ) {
164
176
// The value attribute is interpolated
165
177
optionAttrs . $observe ( 'value' , function valueAttributeObserveAction ( newVal ) {
178
+ var currentVal = self . readValue ( ) ;
179
+ var removal ;
180
+ var previouslySelected = optionElement . prop ( 'selected' ) ;
181
+ var removedVal ;
182
+
166
183
if ( isDefined ( oldVal ) ) {
167
184
self . removeOption ( oldVal ) ;
185
+ removal = true ;
186
+ removedVal = oldVal ;
168
187
}
169
188
oldVal = newVal ;
170
189
self . addOption ( newVal , optionElement ) ;
190
+
191
+ if ( removal && previouslySelected ) {
192
+ scheduleViewValueUpdate ( ) ;
193
+ }
171
194
} ) ;
172
195
} else if ( interpolateTextFn ) {
173
196
// The text content is interpolated
174
197
optionScope . $watch ( interpolateTextFn , function interpolateWatchAction ( newVal , oldVal ) {
175
198
optionAttrs . $set ( 'value' , newVal ) ;
199
+ var previouslySelected = optionElement . prop ( 'selected' ) ;
176
200
if ( oldVal !== newVal ) {
177
201
self . removeOption ( oldVal ) ;
178
202
}
179
203
self . addOption ( newVal , optionElement ) ;
204
+
205
+ if ( oldVal && previouslySelected ) {
206
+ scheduleViewValueUpdate ( ) ;
207
+ }
180
208
} ) ;
181
209
} else {
182
210
// The value attribute is static
183
211
self . addOption ( optionAttrs . value , optionElement ) ;
184
212
}
185
213
214
+
215
+ var oldDisabled ;
216
+ optionAttrs . $observe ( 'disabled' , function ( newVal ) {
217
+
218
+ // Since model updates will also select disabled options (like ngOptions),
219
+ // we only have to handle options becoming disabled, not enabled
220
+
221
+ if ( newVal === 'true' || newVal && optionElement . prop ( 'selected' ) ) {
222
+ if ( self . multiple ) {
223
+ scheduleViewValueUpdate ( true ) ;
224
+ } else {
225
+ self . ngModelCtrl . $setViewValue ( null ) ;
226
+ self . ngModelCtrl . $render ( ) ;
227
+ }
228
+ oldDisabled = newVal ;
229
+ }
230
+ } ) ;
231
+
186
232
optionElement . on ( '$destroy' , function ( ) {
187
- self . removeOption ( optionAttrs . value ) ;
233
+ var currentValue = self . readValue ( ) ;
234
+ var removeValue = optionAttrs . value ;
235
+
236
+ self . removeOption ( removeValue ) ;
188
237
self . ngModelCtrl . $render ( ) ;
238
+
239
+ if ( self . multiple && currentValue && currentValue . indexOf ( removeValue ) !== - 1 ||
240
+ currentValue === removeValue
241
+ ) {
242
+ // When multiple (selected) options are destroyed at the same time, we don't want
243
+ // to run a model update for each of them. Instead, run a single update in the $$postDigest
244
+ scheduleViewValueUpdate ( true ) ;
245
+ }
189
246
} ) ;
190
247
} ;
191
248
} ] ;
@@ -477,12 +534,13 @@ var selectDirective = function() {
477
534
// we have to add an extra watch since ngModel doesn't work well with arrays - it
478
535
// doesn't trigger rendering if only an item in the array changes.
479
536
if ( attr . multiple ) {
537
+ selectCtrl . multiple = true ;
480
538
481
539
// Read value now needs to check each option to see if it is selected
482
540
selectCtrl . readValue = function readMultipleValue ( ) {
483
541
var array = [ ] ;
484
542
forEach ( element . find ( 'option' ) , function ( option ) {
485
- if ( option . selected ) {
543
+ if ( option . selected && ! option . disabled ) {
486
544
var val = option . value ;
487
545
array . push ( val in selectCtrl . selectValueMap ? selectCtrl . selectValueMap [ val ] : val ) ;
488
546
}
0 commit comments