diff --git a/src/cdk/portal/dom-portal-outlet.ts b/src/cdk/portal/dom-portal-outlet.ts index ee20518ad879..cf03cea8cd08 100644 --- a/src/cdk/portal/dom-portal-outlet.ts +++ b/src/cdk/portal/dom-portal-outlet.ts @@ -7,13 +7,13 @@ */ import { + ApplicationRef, ComponentFactoryResolver, ComponentRef, EmbeddedViewRef, - ApplicationRef, Injector, } from '@angular/core'; -import {BasePortalOutlet, ComponentPortal, TemplatePortal, DomPortal} from './portal'; +import {BasePortalOutlet, ComponentPortal, DomPortal, TemplatePortal} from './portal'; /** * A PortalOutlet for attaching portals to an arbitrary DOM element outside of the Angular @@ -84,7 +84,9 @@ export class DomPortalOutlet extends BasePortalOutlet { */ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { let viewContainer = portal.viewContainerRef; - let viewRef = viewContainer.createEmbeddedView(portal.templateRef, portal.context); + let viewRef = viewContainer.createEmbeddedView(portal.templateRef, portal.context, { + injector: portal.injector, + }); // The method `createEmbeddedView` will add the view as a child of the viewContainer. // But for the DomPortalOutlet the view can be added everywhere in the DOM diff --git a/src/cdk/portal/portal-directives.ts b/src/cdk/portal/portal-directives.ts index 57cceb02a838..0986f3b051cb 100644 --- a/src/cdk/portal/portal-directives.ts +++ b/src/cdk/portal/portal-directives.ts @@ -181,7 +181,9 @@ export class CdkPortalOutlet extends BasePortalOutlet implements OnInit, OnDestr */ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { portal.setAttachedHost(this); - const viewRef = this._viewContainerRef.createEmbeddedView(portal.templateRef, portal.context); + const viewRef = this._viewContainerRef.createEmbeddedView(portal.templateRef, portal.context, { + injector: portal.injector, + }); super.setDisposeFn(() => this._viewContainerRef.clear()); this._attachedPortal = portal; diff --git a/src/cdk/portal/portal.spec.ts b/src/cdk/portal/portal.spec.ts index b71ea5576028..feaf8eed93be 100644 --- a/src/cdk/portal/portal.spec.ts +++ b/src/cdk/portal/portal.spec.ts @@ -66,7 +66,7 @@ describe('Portals', () => { ); }); - it('should load a template into the portal', () => { + it('should load a template into the portal outlet', () => { let testAppComponent = fixture.componentInstance; let hostContainer = fixture.nativeElement.querySelector('.portal-container'); let templatePortal = new TemplatePortal(testAppComponent.templateRef, null!); @@ -76,6 +76,36 @@ describe('Portals', () => { // Expect that the content of the attached portal is present and no context is projected expect(hostContainer.textContent).toContain('Banana'); + expect(hostContainer.textContent).toContain('Pizza'); + expect(hostContainer.textContent).not.toContain('Chocolate'); + expect(testAppComponent.portalOutlet.portal).toBe(templatePortal); + + // We can't test whether it's an instance of an `EmbeddedViewRef` so + // we verify that it's defined and that it's not a ComponentRef. + expect(testAppComponent.portalOutlet.attachedRef instanceof ComponentRef).toBe(false); + expect(testAppComponent.portalOutlet.attachedRef).toBeTruthy(); + expect(testAppComponent.attachedSpy).toHaveBeenCalledWith( + testAppComponent.portalOutlet.attachedRef, + ); + }); + + it('should load a template with a custom injector into the portal outlet', () => { + const testAppComponent = fixture.componentInstance; + const hostContainer = fixture.nativeElement.querySelector('.portal-container'); + const templatePortal = new TemplatePortal( + testAppComponent.templateRef, + null!, + undefined, + new ChocolateInjector(fixture.componentInstance.injector), + ); + + testAppComponent.selectedPortal = templatePortal; + fixture.detectChanges(); + + // Expect that the content of the attached portal is present and no context is projected + expect(hostContainer.textContent).toContain('Banana'); + expect(hostContainer.textContent).toContain('Pizza'); + expect(hostContainer.textContent).toContain('Chocolate'); expect(testAppComponent.portalOutlet.portal).toBe(templatePortal); // We can't test whether it's an instance of an `EmbeddedViewRef` so @@ -264,6 +294,8 @@ describe('Portals', () => { // Expect that the content of the attached portal is present. let hostContainer = fixture.nativeElement.querySelector('.portal-container'); expect(hostContainer.textContent).toContain('Banana'); + expect(hostContainer.textContent).toContain('Pizza'); + expect(hostContainer.textContent).not.toContain('Chocolate'); // When updating the binding value. testAppComponent.fruit = 'Mango'; @@ -750,7 +782,7 @@ class ArbitraryViewContainerRefComponent { Cake
Pie
- {{fruit}} - {{ data?.status }} + {{fruit}} - {{ data?.status }}!
    @@ -758,7 +790,7 @@ class ArbitraryViewContainerRefComponent {
- {{fruit}} - {{ data?.status }}! + {{fruit}} - {{ data?.status }}!
diff --git a/src/cdk/portal/portal.ts b/src/cdk/portal/portal.ts index 46c7aea536ab..2a79759c8942 100644 --- a/src/cdk/portal/portal.ts +++ b/src/cdk/portal/portal.ts @@ -119,20 +119,17 @@ export class ComponentPortal extends Portal> { * A `TemplatePortal` is a portal that represents some embedded template (TemplateRef). */ export class TemplatePortal extends Portal> { - /** The embedded template that will be used to instantiate an embedded View in the host. */ - templateRef: TemplateRef; - - /** Reference to the ViewContainer into which the template will be stamped out. */ - viewContainerRef: ViewContainerRef; - - /** Contextual data to be passed in to the embedded view. */ - context: C | undefined; - - constructor(template: TemplateRef, viewContainerRef: ViewContainerRef, context?: C) { + constructor( + /** The embedded template that will be used to instantiate an embedded View in the host. */ + public templateRef: TemplateRef, + /** Reference to the ViewContainer into which the template will be stamped out. */ + public viewContainerRef: ViewContainerRef, + /** Contextual data to be passed in to the embedded view. */ + public context?: C, + /** The injector to use for the embedded view. */ + public injector?: Injector, + ) { super(); - this.templateRef = template; - this.viewContainerRef = viewContainerRef; - this.context = context; } get origin(): ElementRef { diff --git a/tools/public_api_guard/cdk/portal.md b/tools/public_api_guard/cdk/portal.md index 71b106338faf..3103ade38176 100644 --- a/tools/public_api_guard/cdk/portal.md +++ b/tools/public_api_guard/cdk/portal.md @@ -160,11 +160,16 @@ export interface PortalOutlet { // @public export class TemplatePortal extends Portal> { - constructor(template: TemplateRef, viewContainerRef: ViewContainerRef, context?: C); + constructor( + templateRef: TemplateRef, + viewContainerRef: ViewContainerRef, + context?: C | undefined, + injector?: Injector | undefined); attach(host: PortalOutlet, context?: C | undefined): EmbeddedViewRef; - context: C | undefined; + context?: C | undefined; // (undocumented) detach(): void; + injector?: Injector | undefined; // (undocumented) get origin(): ElementRef; templateRef: TemplateRef;