Skip to content

Commit 40b335c

Browse files
crisbetojelbourn
authored andcommitted
fix(list): form control cleared when list is destroyed (#16005)
Currently we remove values from the selection model if the associated list option is destroyed, however that means that when the entire list is destroyed, the form control would be cleared. In most cases this is fine since it's very likely that the entire view was destroyed, however it becomes problematic when it's only the list being destroyed. These changes add an extra check to ensure that we're not clearing the model on destroy. Fixes #15994.
1 parent 50b9fe2 commit 40b335c

File tree

2 files changed

+30
-3
lines changed

2 files changed

+30
-3
lines changed

src/material/list/selection-list.spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,23 @@ describe('MatSelectionList with forms', () => {
10811081
expect(listOptions[1].selected).toBe(true, 'Expected second option to be selected.');
10821082
expect(listOptions[2].selected).toBe(true, 'Expected third option to be selected.');
10831083
});
1084+
1085+
it('should not clear the form control when the list is destroyed', fakeAsync(() => {
1086+
const option = listOptions[1];
1087+
1088+
option.selected = true;
1089+
fixture.detectChanges();
1090+
1091+
expect(fixture.componentInstance.formControl.value).toEqual(['opt2']);
1092+
1093+
fixture.componentInstance.renderList = false;
1094+
fixture.detectChanges();
1095+
tick();
1096+
fixture.detectChanges();
1097+
1098+
expect(fixture.componentInstance.formControl.value).toEqual(['opt2']);
1099+
}));
1100+
10841101
});
10851102

10861103
describe('preselected values', () => {
@@ -1264,7 +1281,7 @@ class SelectionListWithModel {
12641281

12651282
@Component({
12661283
template: `
1267-
<mat-selection-list [formControl]="formControl">
1284+
<mat-selection-list [formControl]="formControl" *ngIf="renderList">
12681285
<mat-list-option value="opt1">Option 1</mat-list-option>
12691286
<mat-list-option value="opt2">Option 2</mat-list-option>
12701287
<mat-list-option value="opt3">Option 3</mat-list-option>
@@ -1273,6 +1290,7 @@ class SelectionListWithModel {
12731290
})
12741291
class SelectionListWithFormControl {
12751292
formControl = new FormControl();
1293+
renderList = true;
12761294
}
12771295

12781296

src/material/list/selection-list.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@ export class MatListOption extends _MatListOptionMixinBase
200200
if (this.selected) {
201201
// We have to delay this until the next tick in order
202202
// to avoid changed after checked errors.
203-
Promise.resolve().then(() => this.selected = false);
203+
Promise.resolve().then(() => {
204+
this.selected = false;
205+
});
204206
}
205207

206208
const hadFocus = this._hasFocus;
@@ -366,6 +368,9 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
366368
/** View to model callback that should be called if the list or its options lost focus. */
367369
_onTouched: () => void = () => {};
368370

371+
/** Whether the list has been destroyed. */
372+
private _destroyed: boolean;
373+
369374
constructor(private _element: ElementRef<HTMLElement>, @Attribute('tabindex') tabIndex: string) {
370375
super();
371376
this.tabIndex = parseInt(tabIndex) || 0;
@@ -412,6 +417,7 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
412417
}
413418

414419
ngOnDestroy() {
420+
this._destroyed = true;
415421
this._modelChanges.unsubscribe();
416422
}
417423

@@ -495,7 +501,10 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
495501

496502
/** Reports a value change to the ControlValueAccessor */
497503
_reportValueChange() {
498-
if (this.options) {
504+
// Stop reporting value changes after the list has been destroyed. This avoids
505+
// cases where the list might wrongly reset its value once it is removed, but
506+
// the form control is still live.
507+
if (this.options && !this._destroyed) {
499508
this._onChange(this._getSelectedOptionValues());
500509
}
501510
}

0 commit comments

Comments
 (0)