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