@@ -10,10 +10,11 @@ import {
1010 Optional ,
1111 Injector ,
1212 ApplicationRef ,
13+ TemplateRef
1314} from '@angular/core' ;
1415import { CommonModule } from '@angular/common' ;
1516import { TemplatePortalDirective , PortalHostDirective , PortalModule } from './portal-directives' ;
16- import { Portal , ComponentPortal } from './portal' ;
17+ import { Portal , ComponentPortal , TemplatePortal } from './portal' ;
1718import { DomPortalHost } from './dom-portal-host' ;
1819
1920
@@ -45,6 +46,57 @@ describe('Portals', () => {
4546 expect ( hostContainer . textContent ) . toContain ( 'Pizza' ) ;
4647 } ) ;
4748
49+ it ( 'should load a template into the portal' , ( ) => {
50+ let testAppComponent = fixture . debugElement . componentInstance ;
51+ let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
52+
53+ let templatePortal = new TemplatePortal ( testAppComponent . templateRef , null ! ) ;
54+ testAppComponent . selectedPortal = templatePortal ;
55+ fixture . detectChanges ( ) ;
56+ // Expect that the content of the attached portal is present and no context is projected
57+ expect ( hostContainer . textContent ) . toContain ( 'Banana' ) ;
58+ } ) ;
59+
60+ it ( 'should project template context bindings in the portal' , ( ) => {
61+ let testAppComponent = fixture . debugElement . componentInstance ;
62+ let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
63+
64+ // TemplatePortal without context:
65+ let templatePortal = new TemplatePortal ( testAppComponent . templateRef , null ! ) ;
66+ testAppComponent . selectedPortal = templatePortal ;
67+ fixture . detectChanges ( ) ;
68+ // Expect that the content of the attached portal is present and NO context is projected
69+ expect ( hostContainer . textContent ) . toContain ( 'Banana - !' ) ;
70+
71+ // using TemplatePortal.attach method to set context
72+ testAppComponent . selectedPortal = undefined ;
73+ fixture . detectChanges ( ) ;
74+ templatePortal . attach ( testAppComponent . portalHost , { $implicit : { status : 'rotten' } } ) ;
75+ fixture . detectChanges ( ) ;
76+ // Expect that the content of the attached portal is present and context given via the
77+ // attach method is projected
78+ expect ( hostContainer . textContent ) . toContain ( 'Banana - rotten!' ) ;
79+
80+ // using TemplatePortal constructor to set the context
81+ templatePortal =
82+ new TemplatePortal ( testAppComponent . templateRef , null ! , { $implicit : { status : 'fresh' } } ) ;
83+ testAppComponent . selectedPortal = templatePortal ;
84+ fixture . detectChanges ( ) ;
85+ // Expect that the content of the attached portal is present and context given via the
86+ // constructor is projected
87+ expect ( hostContainer . textContent ) . toContain ( 'Banana - fresh!' ) ;
88+
89+ // using TemplatePortal constructor to set the context but also calling attach method with
90+ // context, the latter should take precedence:
91+ testAppComponent . selectedPortal = undefined ;
92+ fixture . detectChanges ( ) ;
93+ templatePortal . attach ( testAppComponent . portalHost , { $implicit : { status : 'rotten' } } ) ;
94+ fixture . detectChanges ( ) ;
95+ // Expect that the content of the attached portal is present and and context given via the
96+ // attach method is projected and get precedence over constructor context
97+ expect ( hostContainer . textContent ) . toContain ( 'Banana - rotten!' ) ;
98+ } ) ;
99+
48100 it ( 'should dispose the host when destroyed' , ( ) => {
49101 // Set the selectedHost to be a ComponentPortal.
50102 let testAppComponent = fixture . debugElement . componentInstance ;
@@ -299,15 +351,15 @@ describe('Portals', () => {
299351 fixture . detectChanges ( ) ;
300352
301353 // Attach the TemplatePortal.
302- testAppComponent . portalWithBinding . attach ( host ) ;
354+ testAppComponent . portalWithBinding . attach ( host , { $implicit : { status : 'fresh' } } ) ;
303355 fixture . detectChanges ( ) ;
304356
305357 // Now that the portal is attached, change detection has to happen again in order
306358 // for the bindings to update.
307359 fixture . detectChanges ( ) ;
308360
309361 // Expect that the content of the attached portal is present.
310- expect ( someDomElement . textContent ) . toContain ( 'Banana' ) ;
362+ expect ( someDomElement . textContent ) . toContain ( 'Banana - fresh ' ) ;
311363
312364 // When updating the binding value.
313365 testAppComponent . fruit = 'Mango' ;
@@ -416,18 +468,22 @@ class ArbitraryViewContainerRefComponent {
416468 <ng-template cdk-portal>Cake</ng-template>
417469
418470 <div *cdk-portal>Pie</div>
419- <ng-template cdk-portal> {{fruit}} </ng-template>
471+ <ng-template cdk-portal let-data > {{fruit}} - {{ data?.status }} </ng-template>
420472
421473 <ng-template cdk-portal>
422474 <ul>
423475 <li *ngFor="let fruitName of fruits"> {{fruitName}} </li>
424476 </ul>
425477 </ng-template>
478+
479+ <ng-template #templateRef let-data> {{fruit}} - {{ data?.status }}!</ng-template>
426480 ` ,
427481} )
428482class PortalTestApp {
429483 @ViewChildren ( TemplatePortalDirective ) portals : QueryList < TemplatePortalDirective > ;
430484 @ViewChild ( PortalHostDirective ) portalHost : PortalHostDirective ;
485+ @ViewChild ( 'templateRef' , { read : TemplateRef } ) templateRef : TemplateRef < any > ;
486+
431487 selectedPortal : Portal < any > ;
432488 fruit : string = 'Banana' ;
433489 fruits = [ 'Apple' , 'Pineapple' , 'Durian' ] ;
@@ -449,6 +505,7 @@ class PortalTestApp {
449505 get portalWithTemplate ( ) {
450506 return this . portals . toArray ( ) [ 3 ] ;
451507 }
508+
452509}
453510
454511// Create a real (non-test) NgModule as a workaround for
0 commit comments