@@ -479,7 +479,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
479
479
}
480
480
481
481
ngOnInit ( ) {
482
- this . _selectionModel = new SelectionModel < MatOption > ( this . multiple , undefined , false ) ;
482
+ this . _selectionModel = new SelectionModel < MatOption > ( this . multiple ) ;
483
483
this . stateChanges . next ( ) ;
484
484
485
485
// We need `distinctUntilChanged` here, because some browsers will
@@ -503,6 +503,11 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
503
503
ngAfterContentInit ( ) {
504
504
this . _initKeyManager ( ) ;
505
505
506
+ this . _selectionModel . onChange ! . pipe ( takeUntil ( this . _destroy ) ) . subscribe ( event => {
507
+ event . added . forEach ( option => option . select ( ) ) ;
508
+ event . removed . forEach ( option => option . deselect ( ) ) ;
509
+ } ) ;
510
+
506
511
this . options . changes . pipe ( startWith ( null ) , takeUntil ( this . _destroy ) ) . subscribe ( ( ) => {
507
512
this . _resetOptions ( ) ;
508
513
this . _initializeSelection ( ) ;
@@ -765,19 +770,18 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
765
770
* Sets the selected option based on a value. If no option can be
766
771
* found with the designated value, the select trigger is cleared.
767
772
*/
768
- private _setSelectionByValue ( value : any | any [ ] , isUserInput = false ) : void {
773
+ private _setSelectionByValue ( value : any | any [ ] ) : void {
769
774
if ( this . multiple && value ) {
770
775
if ( ! Array . isArray ( value ) ) {
771
776
throw getMatSelectNonArrayValueError ( ) ;
772
777
}
773
778
774
- this . _clearSelection ( ) ;
775
- value . forEach ( ( currentValue : any ) => this . _selectValue ( currentValue , isUserInput ) ) ;
779
+ this . _selectionModel . clear ( ) ;
780
+ value . forEach ( ( currentValue : any ) => this . _selectValue ( currentValue ) ) ;
776
781
this . _sortValues ( ) ;
777
782
} else {
778
- this . _clearSelection ( ) ;
779
-
780
- const correspondingOption = this . _selectValue ( value , isUserInput ) ;
783
+ this . _selectionModel . clear ( ) ;
784
+ const correspondingOption = this . _selectValue ( value ) ;
781
785
782
786
// Shift focus to the active item. Note that we shouldn't do this in multiple
783
787
// mode, because we don't know what option the user interacted with last.
@@ -793,7 +797,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
793
797
* Finds and selects and option based on its value.
794
798
* @returns Option that has the corresponding value.
795
799
*/
796
- private _selectValue ( value : any , isUserInput = false ) : MatOption | undefined {
800
+ private _selectValue ( value : any ) : MatOption | undefined {
797
801
const correspondingOption = this . options . find ( ( option : MatOption ) => {
798
802
try {
799
803
// Treat null as a special reset value.
@@ -808,29 +812,12 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
808
812
} ) ;
809
813
810
814
if ( correspondingOption ) {
811
- isUserInput ? correspondingOption . _selectViaInteraction ( ) : correspondingOption . select ( ) ;
812
815
this . _selectionModel . select ( correspondingOption ) ;
813
- this . stateChanges . next ( ) ;
814
816
}
815
817
816
818
return correspondingOption ;
817
819
}
818
820
819
-
820
- /**
821
- * Clears the select trigger and deselects every option in the list.
822
- * @param skip Option that should not be deselected.
823
- */
824
- private _clearSelection ( skip ?: MatOption ) : void {
825
- this . _selectionModel . clear ( ) ;
826
- this . options . forEach ( option => {
827
- if ( option !== skip ) {
828
- option . deselect ( ) ;
829
- }
830
- } ) ;
831
- this . stateChanges . next ( ) ;
832
- }
833
-
834
821
/** Sets up a key manager to listen to keyboard events on the overlay panel. */
835
822
private _initKeyManager ( ) {
836
823
this . _keyManager = new ActiveDescendantKeyManager < MatOption > ( this . options )
@@ -858,16 +845,14 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
858
845
private _resetOptions ( ) : void {
859
846
const changedOrDestroyed = merge ( this . options . changes , this . _destroy ) ;
860
847
861
- this . optionSelectionChanges
862
- . pipe ( takeUntil ( changedOrDestroyed ) , filter ( event => event . isUserInput ) )
863
- . subscribe ( event => {
864
- this . _onSelect ( event . source ) ;
848
+ this . optionSelectionChanges . pipe ( takeUntil ( changedOrDestroyed ) ) . subscribe ( event => {
849
+ this . _onSelect ( event . source , event . isUserInput ) ;
865
850
866
- if ( ! this . multiple && this . _panelOpen ) {
867
- this . close ( ) ;
868
- this . focus ( ) ;
869
- }
870
- } ) ;
851
+ if ( event . isUserInput && ! this . multiple && this . _panelOpen ) {
852
+ this . close ( ) ;
853
+ this . focus ( ) ;
854
+ }
855
+ } ) ;
871
856
872
857
// Listen to changes in the internal state of the options and react accordingly.
873
858
// Handles cases like the labels of the selected options changing.
@@ -882,51 +867,42 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
882
867
}
883
868
884
869
/** Invoked when an option is clicked. */
885
- private _onSelect ( option : MatOption ) : void {
870
+ private _onSelect ( option : MatOption , isUserInput : boolean ) : void {
886
871
const wasSelected = this . _selectionModel . isSelected ( option ) ;
887
872
888
- // TODO(crisbeto): handle blank/null options inside multi-select.
889
- if ( this . multiple ) {
890
- this . _selectionModel . toggle ( option ) ;
891
- this . stateChanges . next ( ) ;
892
- wasSelected ? option . deselect ( ) : option . select ( ) ;
893
- this . _keyManager . setActiveItem ( option ) ;
894
- this . _sortValues ( ) ;
895
-
896
- // In case the user select the option with their mouse, we
897
- // want to restore focus back to the trigger, in order to
898
- // prevent the select keyboard controls from clashing with
899
- // the ones from `mat-option`.
900
- this . focus ( ) ;
873
+ if ( option . value == null ) {
874
+ this . _selectionModel . clear ( ) ;
875
+ this . _propagateChanges ( option . value ) ;
901
876
} else {
902
- this . _clearSelection ( option . value == null ? undefined : option ) ;
903
-
904
- if ( option . value == null ) {
905
- this . _propagateChanges ( option . value ) ;
906
- } else {
907
- this . _selectionModel . select ( option ) ;
908
- this . stateChanges . next ( ) ;
877
+ option . selected ? this . _selectionModel . select ( option ) : this . _selectionModel . deselect ( option ) ;
878
+
879
+ // TODO(crisbeto): handle blank/null options inside multi-select.
880
+ if ( this . multiple ) {
881
+ this . _sortValues ( ) ;
882
+
883
+ if ( isUserInput ) {
884
+ this . _keyManager . setActiveItem ( option ) ;
885
+ // In case the user selected the option with their mouse, we
886
+ // want to restore focus back to the trigger, in order to
887
+ // prevent the select keyboard controls from clashing with
888
+ // the ones from `mat-option`.
889
+ this . focus ( ) ;
890
+ }
909
891
}
910
892
}
911
893
912
894
if ( wasSelected !== this . _selectionModel . isSelected ( option ) ) {
913
895
this . _propagateChanges ( ) ;
914
896
}
915
- }
916
897
917
- /**
918
- * Sorts the model values, ensuring that they keep the same
919
- * order that they have in the panel.
920
- */
921
- private _sortValues ( ) : void {
922
- if ( this . _multiple ) {
923
- this . _selectionModel . clear ( ) ;
898
+ this . stateChanges . next ( ) ;
899
+ }
924
900
925
- this . options . forEach ( option => {
926
- if ( option . selected ) {
927
- this . _selectionModel . select ( option ) ;
928
- }
929
- } ) ;
901
+ /** Sorts the selected values in the selected based on their order in the panel. */
902
+ private _sortValues ( ) {
903
+ if ( this . multiple ) {
904
+ const options = this . options . toArray ( ) ;
905
+ this . _selectionModel . sort ( ( a , b ) => options . indexOf ( a ) - options . indexOf ( b ) ) ;
930
906
this . stateChanges . next ( ) ;
931
907
}
932
908
}
0 commit comments