diff --git a/src/cdk/portal/dom-portal-outlet.ts b/src/cdk/portal/dom-portal-outlet.ts index 0ce61f30f316..302fa72d3a2a 100644 --- a/src/cdk/portal/dom-portal-outlet.ts +++ b/src/cdk/portal/dom-portal-outlet.ts @@ -71,6 +71,7 @@ export class DomPortalOutlet extends BasePortalOutlet { // At this point the component has been instantiated, so we move it to the location in the DOM // where we want it to be rendered. this.outletElement.appendChild(this._getComponentRootNode(componentRef)); + this._attachedPortal = portal; return componentRef; } @@ -102,6 +103,8 @@ export class DomPortalOutlet extends BasePortalOutlet { } })); + this._attachedPortal = portal; + // TODO(jelbourn): Return locals from view. return viewRef; } @@ -130,6 +133,7 @@ export class DomPortalOutlet extends BasePortalOutlet { element.parentNode!.insertBefore(anchorNode, element); this.outletElement.appendChild(element); + this._attachedPortal = portal; super.setDisposeFn(() => { // We can't use `replaceWith` here because IE doesn't support it. diff --git a/src/cdk/portal/portal-directives.ts b/src/cdk/portal/portal-directives.ts index f9e873aa6916..9808e9fb68f9 100644 --- a/src/cdk/portal/portal-directives.ts +++ b/src/cdk/portal/portal-directives.ts @@ -214,6 +214,7 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr portal.setAttachedHost(this); element.parentNode!.insertBefore(anchorNode, element); this._getRootNode().appendChild(element); + this._attachedPortal = portal; super.setDisposeFn(() => { if (anchorNode.parentNode) { diff --git a/src/cdk/portal/portal.spec.ts b/src/cdk/portal/portal.spec.ts index a9bc3991620b..0d14b13a1cba 100644 --- a/src/cdk/portal/portal.spec.ts +++ b/src/cdk/portal/portal.spec.ts @@ -101,6 +101,7 @@ describe('Portals', () => { .not.toBe(initialParent, 'Expected portal to be out of the initial parent on attach.'); expect(hostContainer.contains(innerContent)) .toBe(true, 'Expected content to be inside the outlet on attach.'); + expect(testAppComponent.portalOutlet.hasAttached()).toBe(true); testAppComponent.selectedPortal = undefined; fixture.detectChanges(); @@ -109,6 +110,7 @@ describe('Portals', () => { .toBe(initialParent, 'Expected portal to be back inside initial parent on detach.'); expect(hostContainer.contains(innerContent)) .toBe(false, 'Expected content to be removed from outlet on detach.'); + expect(testAppComponent.portalOutlet.hasAttached()).toBe(false); }); it('should throw when trying to load an element without a parent into a DOM portal', () => { @@ -624,6 +626,30 @@ describe('Portals', () => { expect(() => host.detach()).not.toThrow(); }); + it('should set hasAttached when the various portal types are attached', () => { + const fixture = TestBed.createComponent(PortalTestApp); + fixture.detectChanges(); + const viewContainerRef = fixture.componentInstance.viewContainerRef; + + expect(host.hasAttached()).toBe(false); + + host.attachComponentPortal(new ComponentPortal(PizzaMsg, viewContainerRef)); + expect(host.hasAttached()).toBe(true); + + host.detach(); + expect(host.hasAttached()).toBe(false); + + host.attachTemplatePortal( + new TemplatePortal(fixture.componentInstance.templateRef, viewContainerRef)); + expect(host.hasAttached()).toBe(true); + + host.detach(); + expect(host.hasAttached()).toBe(false); + + host.attachDomPortal(new DomPortal(fixture.componentInstance.domPortalContent)); + expect(host.hasAttached()).toBe(true); + }); + }); }); @@ -730,7 +756,7 @@ class PortalTestApp { fruits = ['Apple', 'Pineapple', 'Durian']; attachedSpy = jasmine.createSpy('attached spy'); - constructor(public injector: Injector) { } + constructor(public viewContainerRef: ViewContainerRef, public injector: Injector) { } get cakePortal() { return this.portals.first;