@@ -261,9 +261,13 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
261261 // Get the value by which we are going to track the option
262262 // if we have a trackFn then use that (passing scope and locals)
263263 // 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+
267271 var displayFn = $parse ( match [ 2 ] || match [ 1 ] ) ;
268272 var groupByFn = $parse ( match [ 3 ] || '' ) ;
269273 var disableWhenFn = $parse ( match [ 4 ] || '' ) ;
@@ -290,6 +294,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
290294
291295 return {
292296 trackBy : trackBy ,
297+ getTrackByValue : getTrackByValue ,
293298 getWatchables : $parse ( valuesFn , function ( values ) {
294299 // Create a collection of things that we would like to watch (watchedArray)
295300 // so that they can all be watched using a single $watchCollection
@@ -299,7 +304,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
299304
300305 Object . keys ( values ) . forEach ( function getWatchable ( key ) {
301306 var locals = getLocals ( values [ key ] , key ) ;
302- var selectValue = getTrackByValue ( values [ key ] , locals ) ;
307+ var selectValue = getTrackByValueFn ( values [ key ] , locals ) ;
303308 watchedArray . push ( selectValue ) ;
304309
305310 // Only need to watch the displayFn if there is a specific label expression
@@ -347,7 +352,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
347352 var value = optionValues [ key ] ;
348353 var locals = getLocals ( value , key ) ;
349354 var viewValue = viewValueFn ( scope , locals ) ;
350- var selectValue = getTrackByValue ( viewValue , locals ) ;
355+ var selectValue = getTrackByValueFn ( viewValue , locals ) ;
351356 var label = displayFn ( scope , locals ) ;
352357 var group = groupByFn ( scope , locals ) ;
353358 var disabled = disableWhenFn ( scope , locals ) ;
@@ -361,7 +366,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
361366 items : optionItems ,
362367 selectValueMap : selectValueMap ,
363368 getOptionFromViewValue : function ( value ) {
364- return selectValueMap [ getTrackByValue ( value , getLocals ( value ) ) ] ;
369+ return selectValueMap [ getTrackByValue ( value ) ] ;
365370 } ,
366371 getViewValueFromOption : function ( option ) {
367372 // 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) {
439444 } ;
440445
441446
442- selectCtrl . writeValue = function writeNgOptionsValue ( value ) {
443- var option = options . getOptionFromViewValue ( value ) ;
447+ // Update the controller methods for multiple selectable options
448+ if ( ! multiple ) {
444449
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 ) ;
449452
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+ }
458462 } else {
459- removeEmptyOption ( ) ;
460- renderUnknownOption ( ) ;
463+ if ( value === null || providedEmptyOption ) {
464+ removeUnknownOption ( ) ;
465+ renderEmptyOption ( ) ;
466+ } else {
467+ removeEmptyOption ( ) ;
468+ renderUnknownOption ( ) ;
469+ }
461470 }
462- }
463- } ;
471+ } ;
464472
465- selectCtrl . readValue = function readNgOptionsValue ( ) {
473+ selectCtrl . readValue = function readNgOptionsValue ( ) {
466474
467- var selectedOption = options . selectValueMap [ selectElement . val ( ) ] ;
475+ var selectedOption = options . selectValueMap [ selectElement . val ( ) ] ;
468476
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+ } ;
476484
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+ }
477493
478- // Update the controller methods for multiple selectable options
479- if ( multiple ) {
494+ } else {
480495
481496 ngModelCtrl . $isEmpty = function ( value ) {
482497 return ! value || value . length === 0 ;
@@ -508,6 +523,22 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
508523
509524 return selections ;
510525 } ;
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+ }
511542 }
512543
513544
@@ -534,11 +565,6 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
534565 // We will re-render the option elements if the option values or labels change
535566 scope . $watchCollection ( ngOptions . getWatchables , updateOptions ) ;
536567
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- }
542568 // ------------------------------------------------------------------ //
543569
544570
0 commit comments