@@ -32,8 +32,8 @@ import {
3232import { ControlValueAccessor , FormGroupDirective , NgControl , NgForm } from '@angular/forms' ;
3333import { CanUpdateErrorState , ErrorStateMatcher , mixinErrorState } from '@angular/material/core' ;
3434import { MatFormFieldControl } from '@angular/material/form-field' ;
35- import { merge , Observable , Subscription } from 'rxjs' ;
36- import { startWith } from 'rxjs/operators' ;
35+ import { merge , Observable , Subject , Subscription } from 'rxjs' ;
36+ import { startWith , takeUntil } from 'rxjs/operators' ;
3737import { MatChip , MatChipEvent , MatChipSelectionChange } from './chip' ;
3838import { MatChipInput } from './chip-input' ;
3939
@@ -102,17 +102,15 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
102102 */
103103 readonly controlType : string = 'mat-chip-list' ;
104104
105- /** When a chip is destroyed, we track the index so we can focus the appropriate next chip. */
106- protected _lastDestroyedIndex : number | null = null ;
107-
108- /** Track which chips we're listening to for focus/destruction. */
109- protected _chipSet : WeakMap < MatChip , boolean > = new WeakMap ( ) ;
110-
111- /** Subscription to tabbing out from the chip list. */
112- private _tabOutSubscription = Subscription . EMPTY ;
105+ /**
106+ * When a chip is destroyed, we store the index of the destroyed chip until the chips
107+ * query list notifies about the update. This is necessary because we cannot determine an
108+ * appropriate chip that should receive focus until the array of chips updated completely.
109+ */
110+ private _lastDestroyedChipIndex : number | null = null ;
113111
114- /** Subscription to changes in the chip list . */
115- private _changeSubscription : Subscription ;
112+ /** Subject that emits when the component has been destroyed . */
113+ private _destroyed = new Subject < void > ( ) ;
116114
117115 /** Subscription to focus changes in the chips. */
118116 private _chipFocusSubscription : Subscription | null ;
@@ -350,13 +348,13 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
350348
351349 // Prevents the chip list from capturing focus and redirecting
352350 // it back to the first chip when the user tabs out.
353- this . _tabOutSubscription = this . _keyManager . tabOut . subscribe ( ( ) => {
351+ this . _keyManager . tabOut . pipe ( takeUntil ( this . _destroyed ) ) . subscribe ( ( ) => {
354352 this . _tabIndex = - 1 ;
355353 setTimeout ( ( ) => this . _tabIndex = this . _userTabIndex || 0 ) ;
356354 } ) ;
357355
358356 // When the list changes, re-subscribe
359- this . _changeSubscription = this . chips . changes . pipe ( startWith ( null ) ) . subscribe ( ( ) => {
357+ this . chips . changes . pipe ( startWith ( null ) , takeUntil ( this . _destroyed ) ) . subscribe ( ( ) => {
360358 this . _resetChips ( ) ;
361359
362360 // Reset chips selected/deselected status
@@ -387,18 +385,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
387385 }
388386
389387 ngOnDestroy ( ) {
390- this . _tabOutSubscription . unsubscribe ( ) ;
391-
392- if ( this . _changeSubscription ) {
393- this . _changeSubscription . unsubscribe ( ) ;
394- }
395-
396- if ( this . _chipRemoveSubscription ) {
397- this . _chipRemoveSubscription . unsubscribe ( ) ;
398- }
388+ this . _destroyed . next ( ) ;
389+ this . _destroyed . complete ( ) ;
390+ this . stateChanges . complete ( ) ;
399391
400392 this . _dropSubscriptions ( ) ;
401- this . stateChanges . complete ( ) ;
402393 }
403394
404395
@@ -507,49 +498,19 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
507498 }
508499
509500 /**
510- * Update key manager's active item when chip is deleted.
511- * If the deleted chip is the last chip in chip list, focus the new last chip.
512- * Otherwise focus the next chip in the list.
513- * Save `_lastDestroyedIndex` so we can set the correct focus.
514- */
515- protected _updateKeyManager ( chip : MatChip ) {
516- let chipIndex : number = this . chips . toArray ( ) . indexOf ( chip ) ;
517- if ( this . _isValidIndex ( chipIndex ) ) {
518- if ( chip . _hasFocus ) {
519- // Check whether the chip is not the last item
520- if ( chipIndex < this . chips . length - 1 ) {
521- this . _keyManager . setActiveItem ( chipIndex ) ;
522- } else if ( chipIndex - 1 >= 0 ) {
523- this . _keyManager . setActiveItem ( chipIndex - 1 ) ;
524- }
525- }
526- if ( this . _keyManager . activeItemIndex === chipIndex ) {
527- this . _lastDestroyedIndex = chipIndex ;
528- }
529- }
530- }
531-
532- /**
533- * Checks to see if a focus chip was recently destroyed so that we can refocus the next closest
534- * one.
501+ * If the amount of chips changed, we need to update the key manager state and make sure
502+ * that to so that we can refocus the
503+ * next closest one.
535504 */
536505 protected _updateFocusForDestroyedChips ( ) {
537- const chipsArray = this . chips . toArray ( ) ;
538-
539- if ( this . _lastDestroyedIndex != null && chipsArray . length > 0 && ( this . focused ||
540- ( this . _keyManager . activeItem && chipsArray . indexOf ( this . _keyManager . activeItem ) === - 1 ) ) ) {
541- // Check whether the destroyed chip was the last item
542- const newFocusIndex = Math . min ( this . _lastDestroyedIndex , chipsArray . length - 1 ) ;
543- this . _keyManager . setActiveItem ( newFocusIndex ) ;
544- const focusChip = this . _keyManager . activeItem ;
545- // Focus the chip
546- if ( focusChip ) {
547- focusChip . focus ( ) ;
548- }
506+ if ( this . _lastDestroyedChipIndex == null || ! this . chips . length ) {
507+ return ;
549508 }
550509
551- // Reset our destroyed index
552- this . _lastDestroyedIndex = null ;
510+ const newChipIndex = Math . min ( this . _lastDestroyedChipIndex , this . chips . length - 1 ) ;
511+
512+ this . _keyManager . setActiveItem ( newChipIndex ) ;
513+ this . _lastDestroyedChipIndex = null ;
553514 }
554515
555516 /**
@@ -702,7 +663,6 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
702663 this . _listenToChipsRemoved ( ) ;
703664 }
704665
705-
706666 private _dropSubscriptions ( ) {
707667 if ( this . _chipFocusSubscription ) {
708668 this . _chipFocusSubscription . unsubscribe ( ) ;
@@ -718,6 +678,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
718678 this . _chipSelectionSubscription . unsubscribe ( ) ;
719679 this . _chipSelectionSubscription = null ;
720680 }
681+
682+ if ( this . _chipRemoveSubscription ) {
683+ this . _chipRemoveSubscription . unsubscribe ( ) ;
684+ this . _chipRemoveSubscription = null ;
685+ }
721686 }
722687
723688 /** Listens to user-generated selection events on each chip. */
@@ -761,7 +726,15 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
761726
762727 private _listenToChipsRemoved ( ) : void {
763728 this . _chipRemoveSubscription = this . chipRemoveChanges . subscribe ( event => {
764- this . _updateKeyManager ( event . chip ) ;
729+ const chip = event . chip ;
730+ const chipIndex = this . chips . toArray ( ) . indexOf ( event . chip ) ;
731+
732+ // In case the chip that will be removed is currently focused, we temporarily store
733+ // the index in order to be able to determine an appropriate sibling chip that will
734+ // receive focus.
735+ if ( this . _isValidIndex ( chipIndex ) && chip . _hasFocus ) {
736+ this . _lastDestroyedChipIndex = chipIndex ;
737+ }
765738 } ) ;
766739 }
767740}
0 commit comments