@@ -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
@@ -498,49 +489,19 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
498
489
}
499
490
500
491
/**
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.
526
495
*/
527
496
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 ;
540
499
}
541
500
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 ;
544
505
}
545
506
546
507
/**
@@ -693,7 +654,6 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
693
654
this . _listenToChipsRemoved ( ) ;
694
655
}
695
656
696
-
697
657
private _dropSubscriptions ( ) {
698
658
if ( this . _chipFocusSubscription ) {
699
659
this . _chipFocusSubscription . unsubscribe ( ) ;
@@ -709,6 +669,11 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
709
669
this . _chipSelectionSubscription . unsubscribe ( ) ;
710
670
this . _chipSelectionSubscription = null ;
711
671
}
672
+
673
+ if ( this . _chipRemoveSubscription ) {
674
+ this . _chipRemoveSubscription . unsubscribe ( ) ;
675
+ this . _chipRemoveSubscription = null ;
676
+ }
712
677
}
713
678
714
679
/** Listens to user-generated selection events on each chip. */
@@ -752,7 +717,15 @@ export class MatChipList extends _MatChipListMixinBase implements MatFormFieldCo
752
717
753
718
private _listenToChipsRemoved ( ) : void {
754
719
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
+ }
756
729
} ) ;
757
730
}
758
731
}
0 commit comments