@@ -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,36 @@ 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+ const initialParent = domPortal . element . parentNode ! ;
86+
87+ expect ( innerContent ) . toBeTruthy ( 'Expected portal content to be rendered.' ) ;
88+ expect ( domPortal . element . contains ( innerContent ) )
89+ . toBe ( true , 'Expected content to be inside portal on init.' ) ;
90+ expect ( hostContainer . contains ( innerContent ) )
91+ . toBe ( false , 'Expected content to be outside of portal outlet.' ) ;
92+
93+ testAppComponent . selectedPortal = domPortal ;
94+ fixture . detectChanges ( ) ;
95+
96+ expect ( domPortal . element . parentNode )
97+ . not . toBe ( initialParent , 'Expected portal to be out of the initial parent on attach.' ) ;
98+ expect ( hostContainer . contains ( innerContent ) )
99+ . toBe ( true , 'Expected content to be inside the outlet on attach.' ) ;
100+
101+ testAppComponent . selectedPortal = undefined ;
102+ fixture . detectChanges ( ) ;
103+
104+ expect ( domPortal . element . parentNode )
105+ . toBe ( initialParent , 'Expected portal to be back inside initial parent on detach.' ) ;
106+ expect ( hostContainer . contains ( innerContent ) )
107+ . toBe ( false , 'Expected content to be removed from outlet on detach.' ) ;
108+ } ) ;
109+
79110 it ( 'should project template context bindings in the portal' , ( ) => {
80111 let testAppComponent = fixture . componentInstance ;
81112 let hostContainer = fixture . nativeElement . querySelector ( '.portal-container' ) ;
@@ -351,7 +382,8 @@ describe('Portals', () => {
351382
352383 beforeEach ( ( ) => {
353384 someDomElement = document . createElement ( 'div' ) ;
354- host = new DomPortalOutlet ( someDomElement , componentFactoryResolver , appRef , injector ) ;
385+ host = new DomPortalOutlet ( someDomElement , componentFactoryResolver , appRef , injector ,
386+ document ) ;
355387
356388 someFixture = TestBed . createComponent ( ArbitraryViewContainerRefComponent ) ;
357389 someViewContainerRef = someFixture . componentInstance . viewContainerRef ;
@@ -502,6 +534,20 @@ describe('Portals', () => {
502534 expect ( spy ) . toHaveBeenCalled ( ) ;
503535 } ) ;
504536
537+ it ( 'should attach and detach a DOM portal' , ( ) => {
538+ const fixture = TestBed . createComponent ( PortalTestApp ) ;
539+ fixture . detectChanges ( ) ;
540+ const portal = new DomPortal ( fixture . componentInstance . domPortalContent ) ;
541+
542+ portal . attach ( host ) ;
543+
544+ expect ( someDomElement . textContent ) . toContain ( 'Hello there' ) ;
545+
546+ host . detach ( ) ;
547+
548+ expect ( someDomElement . textContent ! . trim ( ) ) . toBe ( '' ) ;
549+ } ) ;
550+
505551 } ) ;
506552} ) ;
507553
@@ -559,12 +605,17 @@ class ArbitraryViewContainerRefComponent {
559605 </ng-template>
560606
561607 <ng-template #templateRef let-data> {{fruit}} - {{ data?.status }}!</ng-template>
608+
609+ <div #domPortalContent>
610+ <p class="dom-portal-inner-content">Hello there</p>
611+ </div>
562612 ` ,
563613} )
564614class PortalTestApp {
565615 @ViewChildren ( CdkPortal ) portals : QueryList < CdkPortal > ;
566616 @ViewChild ( CdkPortalOutlet , { static : true } ) portalOutlet : CdkPortalOutlet ;
567- @ViewChild ( 'templateRef' , { read : TemplateRef , static : true } ) templateRef : TemplateRef < any > ;
617+ @ViewChild ( 'templateRef' , { read : TemplateRef , static : true } ) templateRef : TemplateRef < any > ;
618+ @ViewChild ( 'domPortalContent' , { static : true } ) domPortalContent : ElementRef < HTMLElement > ;
568619
569620 selectedPortal : Portal < any > | undefined ;
570621 fruit : string = 'Banana' ;
0 commit comments