Skip to content

Commit 82df181

Browse files
crisbetommalerba
authored andcommitted
fix(selection-list): external changes to selection model not being reflected (#9846)
Fixes the selection list not reflecting external changes made to its `selectedOptions` model. Fixes #9838.
1 parent d4c479b commit 82df181

File tree

2 files changed

+47
-3
lines changed

2 files changed

+47
-3
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,18 @@ describe('MatSelectionList without forms', () => {
347347
expect(list.selectedOptions.isSelected(listOptions[0].componentInstance)).toBe(true);
348348
expect(list.selectedOptions.isSelected(listOptions[2].componentInstance)).toBe(true);
349349
});
350+
351+
it('should update the item selected state when it is selected via the model', () => {
352+
const list: MatSelectionList = selectionList.componentInstance;
353+
const item: MatListOption = listOptions[0].componentInstance;
354+
355+
expect(item.selected).toBe(false);
356+
357+
list.selectedOptions.select(item);
358+
fixture.detectChanges();
359+
360+
expect(item.selected).toBe(true);
361+
});
350362
});
351363

352364
describe('with list option selected', () => {
@@ -715,6 +727,16 @@ describe('MatSelectionList with forms', () => {
715727
expect(fixture.componentInstance.selectedOptions).toEqual(['opt2']);
716728
}));
717729

730+
it('should update the model if an option got selected via the model', fakeAsync(() => {
731+
expect(fixture.componentInstance.selectedOptions).toEqual([]);
732+
733+
selectionList.selectedOptions.select(listOptions[0]);
734+
fixture.detectChanges();
735+
tick();
736+
737+
expect(fixture.componentInstance.selectedOptions).toEqual(['opt1']);
738+
}));
739+
718740
});
719741

720742
describe('and formControl', () => {

src/lib/list/selection-list.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {
4141
mixinTabIndex,
4242
} from '@angular/material/core';
4343
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
44+
import {Subscription} from 'rxjs/Subscription';
4445

4546

4647
/** @docs-private */
@@ -112,8 +113,8 @@ export class MatListOption extends _MatListOptionMixinBase
112113
implements AfterContentInit, OnDestroy, OnInit, FocusableOption, CanDisableRipple {
113114

114115
private _lineSetter: MatLineSetter;
115-
private _selected: boolean = false;
116-
private _disabled: boolean = false;
116+
private _selected = false;
117+
private _disabled = false;
117118

118119
/** Whether the option has focus. */
119120
_hasFocus: boolean = false;
@@ -292,7 +293,7 @@ export class MatListOption extends _MatListOptionMixinBase
292293
changeDetection: ChangeDetectionStrategy.OnPush
293294
})
294295
export class MatSelectionList extends _MatSelectionListMixinBase implements FocusableOption,
295-
CanDisable, CanDisableRipple, HasTabIndex, AfterContentInit, ControlValueAccessor {
296+
CanDisable, CanDisableRipple, HasTabIndex, AfterContentInit, ControlValueAccessor, OnDestroy {
296297

297298
/** The FocusKeyManager which handles focus. */
298299
_keyManager: FocusKeyManager<MatListOption>;
@@ -313,6 +314,8 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
313314
/** Used for storing the values that were assigned before the options were initialized. */
314315
private _tempValues: string[]|null;
315316

317+
private _modelChanges = Subscription.EMPTY;
318+
316319
/** View to model callback that should be called if the list or its options lost focus. */
317320
_onTouched: () => void = () => {};
318321

@@ -329,6 +332,25 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu
329332
this._setOptionsFromValues(this._tempValues);
330333
this._tempValues = null;
331334
}
335+
336+
// Sync external changes to the model back to the options.
337+
this._modelChanges = this.selectedOptions.onChange!.subscribe(event => {
338+
if (event.added) {
339+
for (let item of event.added) {
340+
item.selected = true;
341+
}
342+
}
343+
344+
if (event.removed) {
345+
for (let item of event.removed) {
346+
item.selected = false;
347+
}
348+
}
349+
});
350+
}
351+
352+
ngOnDestroy() {
353+
this._modelChanges.unsubscribe();
332354
}
333355

334356
/** Focus the selection-list. */

0 commit comments

Comments
 (0)