I am attempting to open my component in a new window, similar to this example:
https://stackblitz.com/edit/angular-open-window
However, when the window opens, my component is not displayed and I receive the following error in the console:
Error: Must provide a portal to attach
Below is the code for my Angular 8 component (the example above is based on Angular 7):
Window Component TypeScript:
import {Component, ViewChild, OnInit, ComponentFactoryResolver, ApplicationRef, Injector, OnDestroy, Output, EventEmitter } from '@angular/core';
import {CdkPortal, DomPortalHost} from '@angular/cdk/portal';
@Component({
selector: 'window',
templateUrl: './window.component.html',
styleUrls: ['./window.component.scss']
})
export class WindowComponent implements OnInit, OnDestroy {
@Output() close : EventEmitter<any> = new EventEmitter();
// Step 1: Get a reference to the portal
@ViewChild('CdkPortal', {static: true}) portal: CdkPortal;
// Step 2: Save a reference to the window for closing
private externalWindow = null;
// Step 3: Inject required dependencies for PortalHost
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private applicationRef: ApplicationRef,
private injector: Injector){}
ngOnInit(){
// Step 4: Create an external window
this.externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200');
// Step 5: Create a PortalHost with the body of the new window document
const host = new DomPortalHost(
this.externalWindow.document.body,
this.componentFactoryResolver,
this.applicationRef,
this.injector
);
// Step 6: Attach the portal
host.attach(this.portal);
}
ngOnDestroy(){
// Step 7: Close the window when this component is destroyed
this.externalWindow.close();
debugger;
this.close.emit();
}
}
Window Component HTML:
<ng-container *cdkPortal>
<ng-content></ng-content>
</ng-container>
Parent Component TypeScript:
This parent component contains a flag similar to the example provided:
showPortal = false;
Parent Component HTML:
<button (click)="showPortal = true"></button>;
<window *ngIf="showPortal" (close)="showPortal = false">
<h2>Hello world from another window!!</h2>;
<button (click)="showPortal = false">Close Me!</button>;
</window>
Shared Module where I imported Portal module:
import { MaterialModule } from './../material-module';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { SharedHeaderComponent } from './components/shared-header/shared-header.component';
import { RouterModule } from '@angular/router';
import { NgSelectModule } from '@ng-select/ng-select';
import { SharedFooterComponent } from './components/shared-footer/shared-footer.component';
import { SharedNavbarComponent } from './components/shared-navbar/shared-navbar.component';
import { HospitalDetailsComponent } from './components/hospital-details/hospital-details.component';
import { ApiInterceptor } from './interceptors/api-interceptor';
import { JwtInterceptor } from './interceptors/jwt.interceptor';
import { ErrorInterceptor } from './interceptors/error.interceptor';
import { NgxIntlTelInputModule } from 'ngx-intl-tel-input';
import { SharedSidebarComponent } from './components/shared-sidebar/shared-sidebar.component';
import { LiveVideoPopupComponent } from './components/_popup/live-video-popup/live-video-popup.component';
import { ModalBasicComponent } from './components/modal-basic/modal-basic.component';
import { SubscriberComponent } from './components/subscriber/subscriber.component';
import { PublisherComponent } from './components/publisher/publisher.component';
import { OpentokService } from './services/opentok.service';
import { PortalModule } from '@angular/cdk/portal';
@NgModule({
declarations: [
PublisherComponent,
SubscriberComponent,
SharedHeaderComponent,
SharedFooterComponent,
SharedNavbarComponent,
HospitalDetailsComponent,
SharedSidebarComponent,
LiveVideoPopupComponent,
ModalBasicComponent
],
imports: [
PortalModule,
ReactiveFormsModule,
TranslateModule,
HttpClientModule,
RouterModule,
FormsModule,
CommonModule,
NgSelectModule,
MaterialModule
],
exports: [
PortalModule,
ReactiveFormsModule,
FormsModule,
TranslateModule,
SharedHeaderComponent,
SharedNavbarComponent,
SharedFooterComponent,
PublisherComponent,
SubscriberComponent,
LiveVideoPopupComponent,
ModalBasicComponent,
HttpClientModule,
NgSelectModule,
NgxIntlTelInputModule
],
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
OpentokService
]
})
export class SharedModule { }