diff --git a/src/lib/core/portal/portal-directives.ts b/src/lib/core/portal/portal-directives.ts index 1502239211d6..663be835dac6 100644 --- a/src/lib/core/portal/portal-directives.ts +++ b/src/lib/core/portal/portal-directives.ts @@ -63,14 +63,21 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { return this._portal; } - set portal(p: Portal) { - if (p) { - this._replaceAttachedPortal(p); + set portal(portal: Portal) { + if (this.hasAttached()) { + super.detach(); + } + + if (portal) { + super.attach(portal); } + + this._portal = portal; } ngOnDestroy() { - this.dispose(); + super.dispose(); + this._portal = null; } /** @@ -93,7 +100,9 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { componentFactory, viewContainerRef.length, portal.injector || viewContainerRef.parentInjector); - this.setDisposeFn(() => ref.destroy()); + super.setDisposeFn(() => ref.destroy()); + this._portal = portal; + return ref; } @@ -105,23 +114,12 @@ 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()); + this._portal = portal; // TODO(jelbourn): return locals from view return new Map(); } - - /** Detaches the currently attached Portal (if there is one) and attaches the given Portal. */ - private _replaceAttachedPortal(p: Portal): void { - if (this.hasAttached()) { - this.detach(); - } - - if (p) { - this.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..415756ea729f 100644 --- a/src/lib/core/portal/portal.spec.ts +++ b/src/lib/core/portal/portal.spec.ts @@ -2,6 +2,7 @@ import {inject, ComponentFixture, TestBed, async} from '@angular/core/testing'; import { NgModule, Component, + ViewChild, ViewChildren, QueryList, ViewContainerRef, @@ -10,7 +11,7 @@ import { Injector, ApplicationRef, } from '@angular/core'; -import {TemplatePortalDirective, PortalModule} from './portal-directives'; +import {TemplatePortalDirective, PortalHostDirective, PortalModule} from './portal-directives'; import {Portal, ComponentPortal} from './portal'; import {DomPortalHost} from './dom-portal-host'; @@ -141,6 +142,52 @@ describe('Portals', () => { expect(hostContainer.textContent).toContain('Pizza'); }); + + it('should detach the portal when it is set to null', () => { + let testAppComponent = fixture.debugElement.componentInstance; + testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); + + fixture.detectChanges(); + expect(testAppComponent.portalHost.hasAttached()).toBe(true); + expect(testAppComponent.portalHost.portal).toBe(testAppComponent.selectedPortal); + + testAppComponent.selectedPortal = null; + fixture.detectChanges(); + + expect(testAppComponent.portalHost.hasAttached()).toBe(false); + expect(testAppComponent.portalHost.portal).toBeNull(); + }); + + it('should set the `portal` when attaching a component portal programmatically', () => { + let testAppComponent = fixture.debugElement.componentInstance; + let portal = new ComponentPortal(PizzaMsg); + + testAppComponent.portalHost.attachComponentPortal(portal); + + expect(testAppComponent.portalHost.portal).toBe(portal); + }); + + it('should set the `portal` when attaching a template portal programmatically', () => { + let testAppComponent = fixture.debugElement.componentInstance; + fixture.detectChanges(); + + testAppComponent.portalHost.attachTemplatePortal(testAppComponent.cakePortal); + + expect(testAppComponent.portalHost.portal).toBe(testAppComponent.cakePortal); + }); + + it('should clear the portal reference on destroy', () => { + let testAppComponent = fixture.debugElement.componentInstance; + + testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); + fixture.detectChanges(); + + expect(testAppComponent.portalHost.portal).toBeTruthy(); + + fixture.destroy(); + + expect(testAppComponent.portalHost.portal).toBeNull(); + }); }); describe('DomPortalHost', () => { @@ -331,6 +378,7 @@ class ArbitraryViewContainerRefComponent { }) class PortalTestApp { @ViewChildren(TemplatePortalDirective) portals: QueryList; + @ViewChild(PortalHostDirective) portalHost: PortalHostDirective; selectedPortal: Portal; fruit: string = 'Banana';