@@ -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
@@ -498,49 +489,19 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
498489 }
499490
500491 /**
501- * Update key manager's active item when chip is deleted.
502- * If the deleted chip is the last chip in chip list, focus the new last chip.
503- * Otherwise focus the next chip in the list.
504- * Save `_lastDestroyedIndex` so we can set the correct focus.
505- */
506- protected _updateKeyManager ( chip : MatChip ) {
507- let chipIndex : number = this . chips . toArray ( ) . indexOf ( chip ) ;
508- if ( this . _isValidIndex ( chipIndex ) ) {
509- if ( chip . _hasFocus ) {
510- // Check whether the chip is not the last item
511- if ( chipIndex < this . chips . length - 1 ) {
512- this . _keyManager . setActiveItem ( chipIndex ) ;
513- } else if ( chipIndex - 1 >= 0 ) {
514- this . _keyManager . setActiveItem ( chipIndex - 1 ) ;
515- }
516- }
517- if ( this . _keyManager . activeItemIndex === chipIndex ) {
518- this . _lastDestroyedIndex = chipIndex ;
519- }
520- }
521- }
522-
523- /**
524- * Checks to see if a focus chip was recently destroyed so that we can refocus the next closest
525- * one.
492+ * If the amount of chips changed, we need to update the key manager state and make sure
493+ * that to so that we can refocus the
494+ * next closest one.
526495 */
527496 protected _updateFocusForDestroyedChips ( ) {
528- const chipsArray = this . chips . toArray ( ) ;
529-
530- if ( this . _lastDestroyedIndex != null && chipsArray . length > 0 && ( this . focused ||
531- ( this . _keyManager . activeItem && chipsArray . indexOf ( this . _keyManager . activeItem ) === - 1 ) ) ) {
532- // Check whether the destroyed chip was the last item
533- const newFocusIndex = Math . min ( this . _lastDestroyedIndex , chipsArray . length - 1 ) ;
534- this . _keyManager . setActiveItem ( newFocusIndex ) ;
535- const focusChip = this . _keyManager . activeItem ;
536- // Focus the chip
537- if ( focusChip ) {
538- focusChip . focus ( ) ;
539- }
497+ if ( this . _lastDestroyedChipIndex == null || ! this . chips . length ) {
498+ return ;
540499 }
541500
542- // Reset our destroyed index
543- this . _lastDestroyedIndex = null ;
501+ const newChipIndex = Math . min ( this . _lastDestroyedChipIndex , this . chips . length - 1 ) ;
502+
503+ this . _keyManager . setActiveItem ( newChipIndex ) ;
504+ this . _lastDestroyedChipIndex = null ;
544505 }
545506
546507 /**
@@ -693,7 +654,6 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
693654 this . _listenToChipsRemoved ( ) ;
694655 }
695656
696-
697657 private _dropSubscriptions ( ) {
698658 if ( this . _chipFocusSubscription ) {
699659 this . _chipFocusSubscription . unsubscribe ( ) ;
@@ -709,6 +669,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
709669 this . _chipSelectionSubscription . unsubscribe ( ) ;
710670 this . _chipSelectionSubscription = null ;
711671 }
672+
673+ if ( this . _chipRemoveSubscription ) {
674+ this . _chipRemoveSubscription . unsubscribe ( ) ;
675+ this . _chipRemoveSubscription = null ;
676+ }
712677 }
713678
714679 /** Listens to user-generated selection events on each chip. */
@@ -752,7 +717,15 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
752717
753718 private _listenToChipsRemoved ( ) : void {
754719 this . _chipRemoveSubscription = this . chipRemoveChanges . subscribe ( event => {
755- this . _updateKeyManager ( event . chip ) ;
720+ const chip = event . chip ;
721+ const chipIndex = this . chips . toArray ( ) . indexOf ( event . chip ) ;
722+
723+ // In case the chip that will be removed is currently focused, we temporarily store
724+ // the index in order to be able to determine an appropriate sibling chip that will
725+ // receive focus.
726+ if ( this . _isValidIndex ( chipIndex ) && chip . _hasFocus ) {
727+ this . _lastDestroyedChipIndex = chipIndex ;
728+ }
756729 } ) ;
757730 }
758731}
0 commit comments