@@ -261,9 +261,13 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
261
261
// Get the value by which we are going to track the option
262
262
// if we have a trackFn then use that (passing scope and locals)
263
263
// otherwise just hash the given viewValue
264
- var getTrackByValue = trackBy ?
265
- function ( viewValue , locals ) { return trackByFn ( scope , locals ) ; } :
266
- function getHashOfValue ( viewValue ) { return hashKey ( viewValue ) ; } ;
264
+ var getTrackByValueFn = trackBy ?
265
+ function ( value , locals ) { return trackByFn ( scope , locals ) ; } :
266
+ function getHashOfValue ( value ) { return hashKey ( value ) ; } ;
267
+ var getTrackByValue = function ( value , key ) {
268
+ return getTrackByValueFn ( value , getLocals ( value , key ) ) ;
269
+ } ;
270
+
267
271
var displayFn = $parse ( match [ 2 ] || match [ 1 ] ) ;
268
272
var groupByFn = $parse ( match [ 3 ] || '' ) ;
269
273
var disableWhenFn = $parse ( match [ 4 ] || '' ) ;
@@ -290,6 +294,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
290
294
291
295
return {
292
296
trackBy : trackBy ,
297
+ getTrackByValue : getTrackByValue ,
293
298
getWatchables : $parse ( valuesFn , function ( values ) {
294
299
// Create a collection of things that we would like to watch (watchedArray)
295
300
// so that they can all be watched using a single $watchCollection
@@ -299,7 +304,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
299
304
300
305
Object . keys ( values ) . forEach ( function getWatchable ( key ) {
301
306
var locals = getLocals ( values [ key ] , key ) ;
302
- var selectValue = getTrackByValue ( values [ key ] , locals ) ;
307
+ var selectValue = getTrackByValueFn ( values [ key ] , locals ) ;
303
308
watchedArray . push ( selectValue ) ;
304
309
305
310
// Only need to watch the displayFn if there is a specific label expression
@@ -347,7 +352,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
347
352
var value = optionValues [ key ] ;
348
353
var locals = getLocals ( value , key ) ;
349
354
var viewValue = viewValueFn ( scope , locals ) ;
350
- var selectValue = getTrackByValue ( viewValue , locals ) ;
355
+ var selectValue = getTrackByValueFn ( viewValue , locals ) ;
351
356
var label = displayFn ( scope , locals ) ;
352
357
var group = groupByFn ( scope , locals ) ;
353
358
var disabled = disableWhenFn ( scope , locals ) ;
@@ -361,7 +366,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
361
366
items : optionItems ,
362
367
selectValueMap : selectValueMap ,
363
368
getOptionFromViewValue : function ( value ) {
364
- return selectValueMap [ getTrackByValue ( value , getLocals ( value ) ) ] ;
369
+ return selectValueMap [ getTrackByValue ( value ) ] ;
365
370
} ,
366
371
getViewValueFromOption : function ( option ) {
367
372
// If the viewValue could be an object that may be mutated by the application,
@@ -439,44 +444,54 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
439
444
} ;
440
445
441
446
442
- selectCtrl . writeValue = function writeNgOptionsValue ( value ) {
443
- var option = options . getOptionFromViewValue ( value ) ;
447
+ // Update the controller methods for multiple selectable options
448
+ if ( ! multiple ) {
444
449
445
- if ( option && ! option . disabled ) {
446
- if ( selectElement [ 0 ] . value !== option . selectValue ) {
447
- removeUnknownOption ( ) ;
448
- removeEmptyOption ( ) ;
450
+ selectCtrl . writeValue = function writeNgOptionsValue ( value ) {
451
+ var option = options . getOptionFromViewValue ( value ) ;
449
452
450
- selectElement [ 0 ] . value = option . selectValue ;
451
- option . element . selected = true ;
452
- option . element . setAttribute ( 'selected' , 'selected' ) ;
453
- }
454
- } else {
455
- if ( value === null || providedEmptyOption ) {
456
- removeUnknownOption ( ) ;
457
- renderEmptyOption ( ) ;
453
+ if ( option && ! option . disabled ) {
454
+ if ( selectElement [ 0 ] . value !== option . selectValue ) {
455
+ removeUnknownOption ( ) ;
456
+ removeEmptyOption ( ) ;
457
+
458
+ selectElement [ 0 ] . value = option . selectValue ;
459
+ option . element . selected = true ;
460
+ option . element . setAttribute ( 'selected' , 'selected' ) ;
461
+ }
458
462
} else {
459
- removeEmptyOption ( ) ;
460
- renderUnknownOption ( ) ;
463
+ if ( value === null || providedEmptyOption ) {
464
+ removeUnknownOption ( ) ;
465
+ renderEmptyOption ( ) ;
466
+ } else {
467
+ removeEmptyOption ( ) ;
468
+ renderUnknownOption ( ) ;
469
+ }
461
470
}
462
- }
463
- } ;
471
+ } ;
464
472
465
- selectCtrl . readValue = function readNgOptionsValue ( ) {
473
+ selectCtrl . readValue = function readNgOptionsValue ( ) {
466
474
467
- var selectedOption = options . selectValueMap [ selectElement . val ( ) ] ;
475
+ var selectedOption = options . selectValueMap [ selectElement . val ( ) ] ;
468
476
469
- if ( selectedOption && ! selectedOption . disabled ) {
470
- removeEmptyOption ( ) ;
471
- removeUnknownOption ( ) ;
472
- return options . getViewValueFromOption ( selectedOption ) ;
473
- }
474
- return null ;
475
- } ;
477
+ if ( selectedOption && ! selectedOption . disabled ) {
478
+ removeEmptyOption ( ) ;
479
+ removeUnknownOption ( ) ;
480
+ return options . getViewValueFromOption ( selectedOption ) ;
481
+ }
482
+ return null ;
483
+ } ;
476
484
485
+ // If we are using `track by` then we must watch the tracked value on the model
486
+ // since ngModel only watches for object identity change
487
+ if ( ngOptions . trackBy ) {
488
+ scope . $watch (
489
+ function ( ) { return ngOptions . getTrackByValue ( ngModelCtrl . $viewValue ) ; } ,
490
+ function ( ) { ngModelCtrl . $render ( ) ; }
491
+ ) ;
492
+ }
477
493
478
- // Update the controller methods for multiple selectable options
479
- if ( multiple ) {
494
+ } else {
480
495
481
496
ngModelCtrl . $isEmpty = function ( value ) {
482
497
return ! value || value . length === 0 ;
@@ -508,6 +523,22 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
508
523
509
524
return selections ;
510
525
} ;
526
+
527
+ // If we are using `track by` then we must watch these tracked values on the model
528
+ // since ngModel only watches for object identity change
529
+ if ( ngOptions . trackBy ) {
530
+
531
+ scope . $watchCollection ( function ( ) {
532
+ if ( isArray ( ngModelCtrl . $viewValue ) ) {
533
+ return ngModelCtrl . $viewValue . map ( function ( value ) {
534
+ return ngOptions . getTrackByValue ( value ) ;
535
+ } ) ;
536
+ }
537
+ } , function ( ) {
538
+ ngModelCtrl . $render ( ) ;
539
+ } ) ;
540
+
541
+ }
511
542
}
512
543
513
544
@@ -534,11 +565,6 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
534
565
// We will re-render the option elements if the option values or labels change
535
566
scope . $watchCollection ( ngOptions . getWatchables , updateOptions ) ;
536
567
537
- // We also need to watch to see if the internals of the model changes, since
538
- // ngModel only watches for object identity change
539
- if ( ngOptions . trackBy ) {
540
- scope . $watchCollection ( attr . ngModel , function ( ) { ngModelCtrl . $render ( ) ; } ) ;
541
- }
542
568
// ------------------------------------------------------------------ //
543
569
544
570
0 commit comments