Trouble arises when trying to open a new window using the Angular 8 CDK

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 { }

Answer №1

There are two ways to resolve this issue:

1. Move your code to ngAfterViewInit() so that it allows time for the component to load:

ngAfterViewInit() {
// Step 4: Open an external window
this.externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200');

// Step 5: Create a PortalHost using 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);
}

2. Alternatively, use setTimeOut within ngOnInit() for delayed execution:

ngOnInit() {
setTimeout(() => {
  // Step 4: Open an external window
  this.externalWindow = window.open('', '', 'width=600,height=400,left=200,top=200');

  // Step 5: Create a PortalHost using 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);
}, 1000);
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using a self-invoking function in JavaScript with addEventListener

I'm struggling to get an Event Listener to self invoke a function and work correctly. Although the following code runs the function, the Event Listener is not functioning as expected: window.addEventListener("resize", (function () { document.getElem ...

What is the best way to connect a string to a scoped variable in a view?

I'm looking to connect a string to a scope variable that will be determined by user input in an input field located within a specific view. The goal is for the combined string and scope variable value to appear in another div within the view. Here&ap ...

The OnChange event seems to be malfunctioning as it is not being triggered despite other parts of the code functioning properly

Check out the code snippet below: import React, { useState } from "react"; function IP() { const [ipAddress, setIPAddress] = useState(""); const handleInputChange = (event) => { const inputValue = event.target.value; // ...

Learn how to creatively style buttons with dynamic effects using tailwindcss

My Desired Button: I have a Button component that can accept a variant prop. My goal is to have the button's className change dynamically based on the prop passed to it. Instead of using if/else statements for different buttons, I want to use a sing ...

Displaying multiple div elements when a button is clickedLet multiple divs be

ISSUE Hello there! I'm facing a challenge where I need to display a div when a button is clicked. The issue arises from having multiple divs with the same class, making it difficult for me to target each individual div... Image1 Image2 Desired Outco ...

In the virtual playground of Plaid's Sandbox, how can I replicate a fresh transaction and detect it using the Webhook feature?

Is there a way to trigger a simulated transaction within the webhook instead of just a DEFAULT_UPDATE event? I'm trying to find a way to simulate an actual transaction so that I can test my webhook integration. I've searched through the sandbox ...

Utilizing jQuery to fetch the source value of an image when the closest radio button is selected

On my website, I have a collection of divs that display color swatches in thumbnail size images. What I want to achieve is updating the main product image when a user clicks on a radio button by fetching the source value of the image inside the label eleme ...

Send a request to templateUrl

After experimenting with AngularJS, I decided to create a dynamic route system that funnels all routes through a single PHP file. This was motivated by my desire to prevent users from accessing raw templateUrl files and seeing unstyled partial pages. Prio ...

Error in TypeScript: The type 'Color' cannot be assigned to the type '"default" | "primary" | "secondary"'

Currently, I am utilizing MUI along with typescript and encountering the following issue. It seems like I may be overlooking a fundamental concept here but unfortunately cannot pinpoint it. Error: Type 'Color' is not assignable to type '&quo ...

Display the variance in values present in an array using Node.js

I am facing a challenge and need help with printing arrays that contain both same and different values. I want to compare two arrays and print the values that are present in both arrays in one array, while also creating another array for the differing val ...

Exploring the properties of individual Vue components on a single page with v-for loop

Struggling to render a Vue component in a Rails app by iterating through an array of data fetched via Ajax. The Slim template (index.html.slim) for the index page includes a custom_form_item component, where items represent custom forms data from Rails and ...

I encountered an error while trying to access an Angular 2 CLI hosted on Azure that said, "Permission denied to view this page or directory."

Currently in the process of learning angular 2, I've decided to host an app on Azure using git. Despite having no prior knowledge of git, I am following instructions from this video. This is not my first time hosting a simple app. Upon pushing my app ...

Is it possible for an Angular2 HTTP request to retrieve the response body as binary data?

I'm facing an issue with a URL that returns HTML content with charset=iso-8859-7. Angular's HTTP request converts the data to utf8 by default, making it difficult for me to encode them back in iso-8859-7 properly. Upon researching, I discovered t ...

Refreshing a single HTML element in ASP.NET MVC - the simple way!

Recently, I put together an image gallery using the unite gallery jquery plugin and now I want to change up the images it displays. My plan is to have a button labeled "art" that, when clicked, triggers a function to update the directory path and load ne ...

Angular 2 fails to redirect to a 404 page if both the route parameter and address are not valid

Currently, while working on my application with Angular 4.1.1, I have a habit of declaring routing in every module I create. For instance, in the file new-cars.routing.module.ts: import { NgModule } from '@angular/core'; import { RouterModule, ...

Troubles with displaying Google Maps on Ionic Modal

Having trouble displaying the google map in an ionic modal - it shows up fine on the page but not in the modal. Any help would be greatly appreciated, as this is quite frustrating. Below is my controller js and code for the ionic modal. $ionicModal.from ...

Attempting to refresh the choices in a dropdown list by sending a request via jQuery leads to specific

I have a Dropdown on my View that looks like this... <div class="editor-field"> @Html.DropDownListFor(model => model.CategoryId, new SelectList(new List<string>(), ""), "Select ...") </div> Within my controller, there is an action ...

Troubleshooting: Angular2 fails to send HTTP GET requests

There seems to be an issue with the code provided in (A) where it fails to send its HTTP request when `test()` is called. In contrast, the similar code in (B) functions properly. Do you have any insights into what might be causing this problem? I can confi ...

Troubleshooting Java REST service integration in AngularJS for UPDATE and DELETE operations

After successfully implementing a REST service with Java and testing all HTTP methods using Postman, I decided to explore AngularJS. Upon integrating it to consume the REST service, I encountered issues specifically with the Delete and Put methods not func ...

Adjusting the Stripe Button to Utilize a Unique Button Class

I have a stripe button on my website and I want to add my custom class to it. I discovered that manually creating the CSS for it can work, but I prefer to maintain consistency across all buttons on my site by using my custom class. No matter what I attemp ...