diff --git a/src/lib/core/portal/portal-directives.ts b/src/lib/core/portal/portal-directives.ts index 1502239211d6..4db6cb366b23 100644 --- a/src/lib/core/portal/portal-directives.ts +++ b/src/lib/core/portal/portal-directives.ts @@ -70,7 +70,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { } ngOnDestroy() { - this.dispose(); + super.dispose(); } /** @@ -93,7 +93,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { componentFactory, viewContainerRef.length, portal.injector || viewContainerRef.parentInjector); - this.setDisposeFn(() => ref.destroy()); + super.setDisposeFn(() => ref.destroy()); return ref; } @@ -105,7 +105,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { portal.setAttachedHost(this); this._viewContainerRef.createEmbeddedView(portal.templateRef); - this.setDisposeFn(() => this._viewContainerRef.clear()); + super.setDisposeFn(() => this._viewContainerRef.clear()); // TODO(jelbourn): return locals from view return new Map(); @@ -114,11 +114,11 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { /** Detaches the currently attached Portal (if there is one) and attaches the given Portal. */ private _replaceAttachedPortal(p: Portal): void { if (this.hasAttached()) { - this.detach(); + super.detach(); } if (p) { - this.attach(p); + super.attach(p); this._portal = p; } } diff --git a/src/lib/core/portal/portal.spec.ts b/src/lib/core/portal/portal.spec.ts index ab0b56919aa5..1c4bc9f7d33d 100644 --- a/src/lib/core/portal/portal.spec.ts +++ b/src/lib/core/portal/portal.spec.ts @@ -278,6 +278,17 @@ describe('Portals', () => { expect(someDomElement.innerHTML) .toBe('', 'Expected the DomPortalHost to be empty after detach'); }); + + it('should call the dispose function even if the host has no attached content', () => { + let spy = jasmine.createSpy('host dispose spy'); + + expect(host.hasAttached()).toBe(false, 'Expected host not to have attached content.'); + + host.setDisposeFn(spy); + host.dispose(); + + expect(spy).toHaveBeenCalled(); + }); }); }); diff --git a/src/lib/core/portal/portal.ts b/src/lib/core/portal/portal.ts index 8a083395cc18..97638f872071 100644 --- a/src/lib/core/portal/portal.ts +++ b/src/lib/core/portal/portal.ts @@ -162,12 +162,12 @@ export abstract class BasePortalHost implements PortalHost { private _isDisposed: boolean = false; /** Whether this host has an attached portal. */ - hasAttached() { - return this._attachedPortal != null; + hasAttached(): boolean { + return !!this._attachedPortal; } attach(portal: Portal): any { - if (portal == null) { + if (!portal) { throw new NullPortalError(); } @@ -195,13 +195,12 @@ export abstract class BasePortalHost implements PortalHost { abstract attachTemplatePortal(portal: TemplatePortal): Map; detach(): void { - if (this._attachedPortal) { this._attachedPortal.setAttachedHost(null); } - - this._attachedPortal = null; - if (this._disposeFn != null) { - this._disposeFn(); - this._disposeFn = null; + if (this._attachedPortal) { + this._attachedPortal.setAttachedHost(null); + this._attachedPortal = null; } + + this._invokeDisposeFn(); } dispose() { @@ -209,10 +208,18 @@ export abstract class BasePortalHost implements PortalHost { this.detach(); } + this._invokeDisposeFn(); this._isDisposed = true; } setDisposeFn(fn: () => void) { this._disposeFn = fn; } + + private _invokeDisposeFn() { + if (this._disposeFn) { + this._disposeFn(); + this._disposeFn = null; + } + } } diff --git a/src/lib/dialog/dialog-ref.ts b/src/lib/dialog/dialog-ref.ts index fa70a0b78741..a6fa3adba828 100644 --- a/src/lib/dialog/dialog-ref.ts +++ b/src/lib/dialog/dialog-ref.ts @@ -31,6 +31,7 @@ export class MdDialogRef { this._overlayRef.dispose(); this._afterClosed.next(this._result); this._afterClosed.complete(); + this.componentInstance = null; } }); } diff --git a/src/lib/dialog/dialog.spec.ts b/src/lib/dialog/dialog.spec.ts index 53e67ca2066a..ec156ae7c3f5 100644 --- a/src/lib/dialog/dialog.spec.ts +++ b/src/lib/dialog/dialog.spec.ts @@ -345,6 +345,19 @@ describe('MdDialog', () => { }); }); + it('should not keep a reference to the component after the dialog is closed', async(() => { + let dialogRef = dialog.open(PizzaMsg); + + expect(dialogRef.componentInstance).toBeTruthy(); + + dialogRef.close(); + viewContainerFixture.detectChanges(); + + viewContainerFixture.whenStable().then(() => { + expect(dialogRef.componentInstance).toBeFalsy('Expected reference to have been cleared.'); + }); + })); + describe('disableClose option', () => { it('should prevent closing via clicks on the backdrop', () => { dialog.open(PizzaMsg, {