diff --git a/src/lib/list/selection-list.spec.ts b/src/lib/list/selection-list.spec.ts index d76e7de7aac..302d2c5d909 100644 --- a/src/lib/list/selection-list.spec.ts +++ b/src/lib/list/selection-list.spec.ts @@ -751,6 +751,30 @@ describe('MatSelectionList with forms', () => { expect(fixture.componentInstance.selectedOptions).toEqual(['opt1']); })); + it('should not dispatch the model change event if nothing changed using selectAll', () => { + expect(fixture.componentInstance.modelChangeSpy).not.toHaveBeenCalled(); + + selectionListDebug.componentInstance.selectAll(); + fixture.detectChanges(); + + expect(fixture.componentInstance.modelChangeSpy).toHaveBeenCalledTimes(1); + + selectionListDebug.componentInstance.selectAll(); + fixture.detectChanges(); + + expect(fixture.componentInstance.modelChangeSpy).toHaveBeenCalledTimes(1); + }); + + it('should not dispatch the model change event if nothing changed using selectAll', () => { + expect(fixture.componentInstance.modelChangeSpy).not.toHaveBeenCalled(); + + selectionListDebug.componentInstance.deselectAll(); + fixture.detectChanges(); + + expect(fixture.componentInstance.modelChangeSpy).not.toHaveBeenCalled(); + }); + + }); describe('and formControl', () => { @@ -968,13 +992,14 @@ class SelectionListWithTabindexBinding { @Component({ template: ` - + Option 1 Option 2 Option 3 ` }) class SelectionListWithModel { + modelChangeSpy = jasmine.createSpy('model change spy'); selectedOptions: string[] = []; renderLastOption = true; } diff --git a/src/lib/list/selection-list.ts b/src/lib/list/selection-list.ts index 34843721688..de0114b6c92 100644 --- a/src/lib/list/selection-list.ts +++ b/src/lib/list/selection-list.ts @@ -221,10 +221,10 @@ export class MatListOption extends _MatListOptionMixinBase return this._element.nativeElement; } - /** Sets the selected state of the option. */ - _setSelected(selected: boolean) { + /** Sets the selected state of the option. Returns whether the value has changed. */ + _setSelected(selected: boolean): boolean { if (selected === this._selected) { - return; + return false; } this._selected = selected; @@ -236,6 +236,7 @@ export class MatListOption extends _MatListOptionMixinBase } this._changeDetector.markForCheck(); + return true; } } @@ -302,7 +303,6 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu constructor(private _element: ElementRef, @Attribute('tabindex') tabIndex: string) { super(); - this.tabIndex = parseInt(tabIndex) || 0; } @@ -346,14 +346,12 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu /** Selects all of the options. */ selectAll() { - this.options.forEach(option => option._setSelected(true)); - this._reportValueChange(); + this._setAllOptionsSelected(true); } /** Deselects all of the options. */ deselectAll() { - this.options.forEach(option => option._setSelected(false)); - this._reportValueChange(); + this._setAllOptionsSelected(false); } /** Sets the focused option of the selection-list. */ @@ -479,6 +477,26 @@ export class MatSelectionList extends _MatSelectionListMixinBase implements Focu } } + /** + * Sets the selected state on all of the options + * and emits an event if anything changed. + */ + private _setAllOptionsSelected(isSelected: boolean) { + // Keep track of whether anything changed, because we only want to + // emit the changed event when something actually changed. + let hasChanged = false; + + this.options.forEach(option => { + if (option._setSelected(isSelected)) { + hasChanged = true; + } + }); + + if (hasChanged) { + this._reportValueChange(); + } + } + /** * Utility to ensure all indexes are valid. * @param index The index to be checked.