diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 6d3c550022fc..228f047c5fc1 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -306,6 +306,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { */ private _setValueAndClose(event: MdOptionSelectionChange | null): void { if (event) { + this._clearPreviousSelectedOption(event.source); this._setTriggerValue(event.source.value); this._onChange(event.source.value); } @@ -313,6 +314,17 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { this.closePanel(); } + /** + * Clear any previous selected option and emit a selection change event for this option + */ + private _clearPreviousSelectedOption(skip: MdOption) { + this.autocomplete.options.forEach((option) => { + if (option != skip && option.selected) { + option.deselect(); + } + }); + } + private _createOverlay(): void { this._portal = new TemplatePortal(this.autocomplete.template, this._viewContainerRef); this._overlayRef = this._overlay.create(this._getOverlayConfig()); diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 7118f73ae3cb..87c63b3385f6 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -898,6 +898,69 @@ describe('MdAutocomplete', () => { }); + describe('Option selection', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleAutocomplete); + fixture.detectChanges(); + + fixture.componentInstance.trigger.openPanel(); + fixture.detectChanges(); + }); + + it('should deselect any other selected option', async(() => { + let options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[0].click(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let componentOptions = fixture.componentInstance.options.toArray(); + expect(componentOptions[0].selected) + .toBe(true, `Clicked option should be selected.`); + + options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(componentOptions[0].selected) + .toBe(false, `Previous option should not be selected.`); + expect(componentOptions[1].selected) + .toBe(true, `New Clicked option should be selected.`); + + }); + })); + + it('should call deselect only on the previous selected option', async(() => { + let options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[0].click(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + fixture.detectChanges(); + + let componentOptions = fixture.componentInstance.options.toArray(); + componentOptions.forEach(option => spyOn(option, 'deselect')); + + expect(componentOptions[0].selected) + .toBe(true, `Clicked option should be selected.`); + + options = + overlayContainerElement.querySelectorAll('md-option') as NodeListOf; + options[1].click(); + fixture.detectChanges(); + + expect(componentOptions[0].deselect).toHaveBeenCalled(); + componentOptions.slice(1).forEach(option => expect(option.deselect).not.toHaveBeenCalled()); + }); + })); + }); + describe('misc', () => { it('should allow basic use without any forms directives', () => {