@@ -479,7 +479,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
479479 }
480480
481481 ngOnInit ( ) {
482- this . _selectionModel = new SelectionModel < MatOption > ( this . multiple , undefined , false ) ;
482+ this . _selectionModel = new SelectionModel < MatOption > ( this . multiple ) ;
483483 this . stateChanges . next ( ) ;
484484
485485 // We need `distinctUntilChanged` here, because some browsers will
@@ -503,6 +503,11 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
503503 ngAfterContentInit ( ) {
504504 this . _initKeyManager ( ) ;
505505
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+
506511 this . options . changes . pipe ( startWith ( null ) , takeUntil ( this . _destroy ) ) . subscribe ( ( ) => {
507512 this . _resetOptions ( ) ;
508513 this . _initializeSelection ( ) ;
@@ -765,19 +770,18 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
765770 * Sets the selected option based on a value. If no option can be
766771 * found with the designated value, the select trigger is cleared.
767772 */
768- private _setSelectionByValue ( value : any | any [ ] , isUserInput = false ) : void {
773+ private _setSelectionByValue ( value : any | any [ ] ) : void {
769774 if ( this . multiple && value ) {
770775 if ( ! Array . isArray ( value ) ) {
771776 throw getMatSelectNonArrayValueError ( ) ;
772777 }
773778
774- this . _clearSelection ( ) ;
775- value . forEach ( ( currentValue : any ) => this . _selectValue ( currentValue , isUserInput ) ) ;
779+ this . _selectionModel . clear ( ) ;
780+ value . forEach ( ( currentValue : any ) => this . _selectValue ( currentValue ) ) ;
776781 this . _sortValues ( ) ;
777782 } else {
778- this . _clearSelection ( ) ;
779-
780- const correspondingOption = this . _selectValue ( value , isUserInput ) ;
783+ this . _selectionModel . clear ( ) ;
784+ const correspondingOption = this . _selectValue ( value ) ;
781785
782786 // Shift focus to the active item. Note that we shouldn't do this in multiple
783787 // mode, because we don't know what option the user interacted with last.
@@ -793,7 +797,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
793797 * Finds and selects and option based on its value.
794798 * @returns Option that has the corresponding value.
795799 */
796- private _selectValue ( value : any , isUserInput = false ) : MatOption | undefined {
800+ private _selectValue ( value : any ) : MatOption | undefined {
797801 const correspondingOption = this . options . find ( ( option : MatOption ) => {
798802 try {
799803 // Treat null as a special reset value.
@@ -808,29 +812,12 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
808812 } ) ;
809813
810814 if ( correspondingOption ) {
811- isUserInput ? correspondingOption . _selectViaInteraction ( ) : correspondingOption . select ( ) ;
812815 this . _selectionModel . select ( correspondingOption ) ;
813- this . stateChanges . next ( ) ;
814816 }
815817
816818 return correspondingOption ;
817819 }
818820
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-
834821 /** Sets up a key manager to listen to keyboard events on the overlay panel. */
835822 private _initKeyManager ( ) {
836823 this . _keyManager = new ActiveDescendantKeyManager < MatOption > ( this . options )
@@ -858,16 +845,14 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
858845 private _resetOptions ( ) : void {
859846 const changedOrDestroyed = merge ( this . options . changes , this . _destroy ) ;
860847
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 ) ;
865850
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+ } ) ;
871856
872857 // Listen to changes in the internal state of the options and react accordingly.
873858 // Handles cases like the labels of the selected options changing.
@@ -882,51 +867,42 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
882867 }
883868
884869 /** Invoked when an option is clicked. */
885- private _onSelect ( option : MatOption ) : void {
870+ private _onSelect ( option : MatOption , isUserInput : boolean ) : void {
886871 const wasSelected = this . _selectionModel . isSelected ( option ) ;
887872
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 ) ;
901876 } 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+ }
909891 }
910892 }
911893
912894 if ( wasSelected !== this . _selectionModel . isSelected ( option ) ) {
913895 this . _propagateChanges ( ) ;
914896 }
915- }
916897
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+ }
924900
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 ) ) ;
930906 this . stateChanges . next ( ) ;
931907 }
932908 }
0 commit comments