@@ -235,12 +235,13 @@ function SelectDirective($mdSelect, $mdUtil, $mdConstant, $mdTheming, $mdAria, $
235
235
236
236
// Use everything that's left inside element.contents() as the contents of the menu
237
237
var multipleContent = isMultiple ? 'multiple' : '' ;
238
+ var ngModelOptions = attr . ngModelOptions ? $mdUtil . supplant ( 'ng-model-options="{0}"' , [ attr . ngModelOptions ] ) : '' ;
238
239
var selectTemplate = '' +
239
240
'<div class="md-select-menu-container" aria-hidden="true" role="presentation">' +
240
- '<md-select-menu role="presentation" {0}>{1 }</md-select-menu>' +
241
+ '<md-select-menu role="presentation" {0} {1}>{2 }</md-select-menu>' +
241
242
'</div>' ;
242
243
243
- selectTemplate = $mdUtil . supplant ( selectTemplate , [ multipleContent , element . html ( ) ] ) ;
244
+ selectTemplate = $mdUtil . supplant ( selectTemplate , [ multipleContent , ngModelOptions , element . html ( ) ] ) ;
244
245
element . empty ( ) . append ( valueEl ) ;
245
246
element . append ( selectTemplate ) ;
246
247
@@ -742,28 +743,40 @@ function SelectMenuDirective($parse, $mdUtil, $mdConstant, $mdTheming) {
742
743
return ! self . options [ self . hashGetter ( $viewValue ) ] ;
743
744
} ;
744
745
745
- // Allow users to provide `ng-model="foo" ng-model-options="{trackBy: 'foo .id'}"` so
746
+ // Allow users to provide `ng-model="foo" ng-model-options="{trackBy: '$value .id'}"` so
746
747
// that we can properly compare objects set on the model to the available options
747
- var trackByOption = $mdUtil . getModelOption ( ngModel , 'trackBy' ) ;
748
-
749
- if ( trackByOption ) {
750
- var trackByLocals = { } ;
751
- var trackByParsed = $parse ( trackByOption ) ;
752
- self . hashGetter = function ( value , valueScope ) {
753
- trackByLocals . $value = value ;
754
- return trackByParsed ( valueScope || $scope , trackByLocals ) ;
755
- } ;
756
- // If the user doesn't provide a trackBy, we automatically generate an id for every
757
- // value passed in
758
- } else {
759
- self . hashGetter = function getHashValue ( value ) {
760
- if ( angular . isObject ( value ) ) {
761
- return 'object_' + ( value . $$mdSelectId || ( value . $$mdSelectId = ++ selectNextId ) ) ;
748
+ //
749
+ // If the user doesn't provide a trackBy, we automatically generate an id for every
750
+ // value passed in with the getId function
751
+ if ( $attrs . ngModelOptions ) {
752
+ self . hashGetter = function ( value ) {
753
+ var ngModelOptions = $parse ( $attrs . ngModelOptions ) ( $scope ) ;
754
+ var trackByOption = ngModelOptions && ngModelOptions . trackBy ;
755
+
756
+ if ( trackByOption ) {
757
+ return $parse ( trackByOption ) ( $scope , { $value : value } ) ;
758
+ } else if ( angular . isObject ( value ) ) {
759
+ return getId ( value ) ;
762
760
}
763
761
return value ;
764
762
} ;
763
+ } else {
764
+ self . hashGetter = getId ;
765
765
}
766
766
self . setMultiple ( self . isMultiple ) ;
767
+
768
+ /**
769
+ * If the value is an object, get the unique, incremental id of the value.
770
+ * If it's not an object, the value will be converted to a string and then returned.
771
+ * @param value
772
+ * @returns {string }
773
+ */
774
+ function getId ( value ) {
775
+ if ( angular . isObject ( value ) && ! angular . isArray ( value ) ) {
776
+ return 'object_' + ( value . $$mdSelectId || ( value . $$mdSelectId = ++ selectNextId ) ) ;
777
+ }
778
+ return value + '' ;
779
+ }
767
780
} ;
768
781
769
782
self . selectedLabels = function ( opts ) {
@@ -867,15 +880,41 @@ function SelectMenuDirective($parse, $mdUtil, $mdConstant, $mdTheming) {
867
880
values . push ( self . selected [ hashKey ] ) ;
868
881
}
869
882
}
870
- var usingTrackBy = $mdUtil . getModelOption ( self . ngModel , 'trackBy' ) ;
871
883
872
884
var newVal = self . isMultiple ? values : values [ 0 ] ;
873
885
var prevVal = self . ngModel . $modelValue ;
874
886
875
- if ( usingTrackBy ? ! angular . equals ( prevVal , newVal ) : ( prevVal + '' ) !== newVal ) {
887
+ if ( ! equals ( prevVal , newVal ) ) {
876
888
self . ngModel . $setViewValue ( newVal ) ;
877
889
self . ngModel . $render ( ) ;
878
890
}
891
+
892
+ function equals ( prevVal , newVal ) {
893
+ if ( self . isMultiple ) {
894
+ if ( ! angular . isArray ( prevVal ) ) {
895
+ // newVal is always an array when self.isMultiple is true
896
+ // thus, if prevVal is not an array they are different
897
+ return false ;
898
+ } else if ( prevVal . length !== newVal . length ) {
899
+ // they are different if they have different length
900
+ return false ;
901
+ } else {
902
+ // if they have the same length, then they are different
903
+ // if an item in the newVal array can't be found in the prevVal
904
+ var prevValHashes = prevVal . map ( function ( prevValItem ) {
905
+ return self . hashGetter ( prevValItem ) ;
906
+ } ) ;
907
+ return newVal . every ( function ( newValItem ) {
908
+ var newValItemHash = self . hashGetter ( newValItem ) ;
909
+ return prevValHashes . some ( function ( prevValHash ) {
910
+ return prevValHash === newValItemHash ;
911
+ } ) ;
912
+ } ) ;
913
+ }
914
+ } else {
915
+ return self . hashGetter ( prevVal ) === self . hashGetter ( newVal ) ;
916
+ }
917
+ }
879
918
} ;
880
919
881
920
function renderMultiple ( ) {
0 commit comments