@@ -32,8 +32,8 @@ import {
32
32
import { ControlValueAccessor , FormGroupDirective , NgControl , NgForm } from '@angular/forms' ;
33
33
import { CanUpdateErrorState , ErrorStateMatcher , mixinErrorState } from '@angular/material/core' ;
34
34
import { 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' ;
37
37
import { MatChip , MatChipEvent , MatChipSelectionChange } from './chip' ;
38
38
import { MatChipInput } from './chip-input' ;
39
39
@@ -102,17 +102,15 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
102
102
*/
103
103
readonly controlType : string = 'mat-chip-list' ;
104
104
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 ;
113
111
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 > ( ) ;
116
114
117
115
/** Subscription to focus changes in the chips. */
118
116
private _chipFocusSubscription : Subscription | null ;
@@ -350,13 +348,13 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
350
348
351
349
// Prevents the chip list from capturing focus and redirecting
352
350
// 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 ( ( ) => {
354
352
this . _tabIndex = - 1 ;
355
353
setTimeout ( ( ) => this . _tabIndex = this . _userTabIndex || 0 ) ;
356
354
} ) ;
357
355
358
356
// 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 ( ( ) => {
360
358
this . _resetChips ( ) ;
361
359
362
360
// Reset chips selected/deselected status
@@ -387,18 +385,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
387
385
}
388
386
389
387
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 ( ) ;
399
391
400
392
this . _dropSubscriptions ( ) ;
401
- this . stateChanges . complete ( ) ;
402
393
}
403
394
404
395
@@ -507,49 +498,19 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
507
498
}
508
499
509
500
/**
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.
535
504
*/
536
505
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 ;
549
508
}
550
509
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 ;
553
514
}
554
515
555
516
/**
@@ -702,7 +663,6 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
702
663
this . _listenToChipsRemoved ( ) ;
703
664
}
704
665
705
-
706
666
private _dropSubscriptions ( ) {
707
667
if ( this . _chipFocusSubscription ) {
708
668
this . _chipFocusSubscription . unsubscribe ( ) ;
@@ -718,6 +678,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
718
678
this . _chipSelectionSubscription . unsubscribe ( ) ;
719
679
this . _chipSelectionSubscription = null ;
720
680
}
681
+
682
+ if ( this . _chipRemoveSubscription ) {
683
+ this . _chipRemoveSubscription . unsubscribe ( ) ;
684
+ this . _chipRemoveSubscription = null ;
685
+ }
721
686
}
722
687
723
688
/** Listens to user-generated selection events on each chip. */
@@ -761,7 +726,15 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
761
726
762
727
private _listenToChipsRemoved ( ) : void {
763
728
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
+ }
765
738
} ) ;
766
739
}
767
740
}
0 commit comments