@@ -12,10 +12,11 @@ import {
1212 ApplicationRef ,
1313 TemplateRef ,
1414 ComponentRef ,
15+ ElementRef ,
1516} from '@angular/core' ;
1617import { CommonModule } from '@angular/common' ;
1718import { CdkPortal , CdkPortalOutlet , PortalModule } from './portal-directives' ;
18- import { Portal , ComponentPortal , TemplatePortal } from './portal' ;
19+ import { Portal , ComponentPortal , TemplatePortal , DomPortal } from './portal' ;
1920import { DomPortalOutlet } from './dom-portal-outlet' ;
2021
2122
@@ -76,6 +77,35 @@ describe('Portals', () => {
7677 . toHaveBeenCalledWith ( testAppComponent . portalOutlet . attachedRef ) ;
7778 } ) ;
7879
80+ it ( 'should load a DOM portal' , ( ) => {
81+ const testAppComponent = fixture . componentInstance ;
82+ const hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
83+ const innerContent = fixture . nativeElement . querySelector ( '.dom-portal-inner-content' ) ;
84+ const domPortal = new DomPortal ( testAppComponent . domPortalContent ) ;
85+
86+ expect ( innerContent ) . toBeTruthy ( 'Expected portal content to be rendered.' ) ;
87+ expect ( domPortal . element . contains ( innerContent ) )
88+ . toBe ( true , 'Expected content to be inside portal on init.' ) ;
89+ expect ( hostContainer . contains ( innerContent ) )
90+ . toBe ( false , 'Expected content to be outside of portal outlet.' ) ;
91+
92+ testAppComponent . selectedPortal = domPortal ;
93+ fixture . detectChanges ( ) ;
94+
95+ expect ( domPortal . element . contains ( innerContent ) )
96+ . toBe ( false , 'Expected content to be out of the portal on attach.' ) ;
97+ expect ( hostContainer . contains ( innerContent ) )
98+ . toBe ( true , 'Expected content to be inside the outlet on attach.' ) ;
99+
100+ testAppComponent . selectedPortal = undefined ;
101+ fixture . detectChanges ( ) ;
102+
103+ expect ( domPortal . element . contains ( innerContent ) )
104+ . toBe ( true , 'Expected content to be at initial position on detach.' ) ;
105+ expect ( hostContainer . contains ( innerContent ) )
106+ . toBe ( false , 'Expected content to be removed from outlet on detach.' ) ;
107+ } ) ;
108+
79109 it ( 'should project template context bindings in the portal' , ( ) => {
80110 let testAppComponent = fixture . componentInstance ;
81111 let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
@@ -502,6 +532,20 @@ describe('Portals', () => {
502532 expect ( spy ) . toHaveBeenCalled ( ) ;
503533 } ) ;
504534
535+ it ( 'should attach and detach a DOM portal' , ( ) => {
536+ const fixture = TestBed . createComponent ( PortalTestApp ) ;
537+ fixture . detectChanges ( ) ;
538+ const portal = new DomPortal ( fixture . componentInstance . domPortalContent ) ;
539+
540+ portal . attach ( host ) ;
541+
542+ expect ( someDomElement . textContent ) . toContain ( 'Hello there' ) ;
543+
544+ host . detach ( ) ;
545+
546+ expect ( someDomElement . textContent ! . trim ( ) ) . toBe ( '' ) ;
547+ } ) ;
548+
505549 } ) ;
506550} ) ;
507551
@@ -559,12 +603,17 @@ class ArbitraryViewContainerRefComponent {
559603 </ng-template>
560604
561605 <ng-template #templateRef let-data> {{fruit}} - {{ data?.status }}!</ng-template>
606+
607+ <div #domPortalContent>
608+ <p class="dom-portal-inner-content">Hello there</p>
609+ </div>
562610 ` ,
563611} )
564612class PortalTestApp {
565613 @ViewChildren ( CdkPortal ) portals : QueryList < CdkPortal > ;
566614 @ViewChild ( CdkPortalOutlet , { static : true } ) portalOutlet : CdkPortalOutlet ;
567- @ViewChild ( 'templateRef' , { read : TemplateRef , static : true } ) templateRef : TemplateRef < any > ;
615+ @ViewChild ( 'templateRef' , { read : TemplateRef , static : true } ) templateRef : TemplateRef < any > ;
616+ @ViewChild ( 'domPortalContent' , { static : true } ) domPortalContent : ElementRef < HTMLElement > ;
568617
569618 selectedPortal : Portal < any > | undefined ;
570619 fruit : string = 'Banana' ;
0 commit comments