From 880e6d513d8b057a681a074b9959ed2052dc6e96 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sun, 9 Jul 2017 06:10:43 +0200 Subject: [PATCH] fix(autocomplete): error when closing destroyed panel (#5446) Fixes an error that was being thrown when attempting to close the panel of an autocomplete that is inside a destroyed view. The issue is that `closePanel` can end up getting called multiple times in a row, which leads to an attempt to call `detectChanges` on a destroyed `ChangeDetectorRef`. Fixes #5413. --- src/lib/autocomplete/autocomplete-trigger.ts | 4 +++ src/lib/autocomplete/autocomplete.spec.ts | 28 +++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index ba175e64dd5..475a45fa5b0 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -170,6 +170,10 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { /** Closes the autocomplete suggestion panel. */ closePanel(): void { + if (!this.panelOpen) { + return; + } + if (this._overlayRef && this._overlayRef.hasAttached()) { this._overlayRef.detach(); this._closingActionsSubscription.unsubscribe(); diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index dbc981d60a9..de12a3fb799 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -221,17 +221,31 @@ describe('MdAutocomplete', () => { }); })); - it('should close the panel programmatically', () => { + it('should close the panel programmatically', async(() => { fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); - fixture.componentInstance.trigger.closePanel(); - fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.componentInstance.trigger.closePanel(); + fixture.detectChanges(); - expect(fixture.componentInstance.trigger.panelOpen) - .toBe(false, `Expected closing programmatically to set the panel state to closed.`); - expect(overlayContainerElement.textContent) - .toEqual('', `Expected closing programmatically to close the panel.`); + fixture.whenStable().then(() => { + expect(fixture.componentInstance.trigger.panelOpen) + .toBe(false, `Expected closing programmatically to set the panel state to closed.`); + expect(overlayContainerElement.textContent) + .toEqual('', `Expected closing programmatically to close the panel.`); + }); + }); + })); + + it('should not throw when attempting to close the panel of a destroyed autocomplete', () => { + const trigger = fixture.componentInstance.trigger; + + trigger.openPanel(); + fixture.detectChanges(); + fixture.destroy(); + + expect(() => trigger.closePanel()).not.toThrow(); }); it('should hide the panel when the options list is empty', async(() => {