Error encountered when an Angular expression is changed after it has already been checked in a dynamically generated component

I am encountering a problem with Angular and the CdkPortal/CdkPortalHost from @angular/cdk.
I developed a service that allows me to associate a CdkPortalHost with a specified name and set its Component at any given time.

This is how the service is structured:

private portalHosts : { [location : string] : CdkPortalOutlet } = {};
private portals : { [location : string] : any} = {};

/** Assigns the PortalHost to the designated location. */
public register(location : string, portalHost : CdkPortalOutlet) {
    this.portalHosts[location] = portalHost;
}

/** Sets the Component for the specified location. */
public setComponent<T>(location : string, type : ComponentType<T>) : ComponentRef<T> {
    let ref : ComponentRef<T> = null;
    let portalHost = this.portalHosts[location];
    if (portalHost) {
        if (portalHost.hasAttached()) {
            portalHost.portal.detach();
            this.portals[location] = null;
        }
        ref = portalHost.attachComponentPortal(new ComponentPortal(type));
        this.portals[location] = ref.instance;
    }
    return ref;
}

Next, I registered a PortalHost in this manner:

@ViewChild(PortalHostDirective)
private portalHost : PortalHostDirective;

public ngAfterContentInit() {
    this.dynamicComponentService.register("Location", this.portalHost);
}

In another child Component of the one registering the PortalHost, I initialize the dynamic Component using the following code:

public ngAfterContentInit() {
    this.dynamicComponentService.setComponent("Location", MyDynamicComponent);
}

For testing purposes, I have a simple template for MyDynamicComponent like so:

<div *ngIf="true">Test</div>

Upon running the application, I encounter the error message below:

Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked.
Previous value: 'undefined'. Current value: 'true'.
It seems like the view has been created after its parent and its children have been dirty checked.
Has it been created in a change detection hook ?

I have looked into similar issues, but the "window.setTimeout" workaround did not resolve the problem for me.
I also attempted different lifecycle hooks for the PortalHost registration and Component creation, but encountered the same issue...

I am currently utilizing Angular 5.0.1 and Material/CDK 5.0.0-rc0.

Am I overlooking something or is this possibly a bug?
Could this be related to Angular/Material#5268

Answer №1

ngAfterContentInit might not be the optimal time for the change detection of the portal outlet to take place. I encountered this issue firsthand when setting up the portal outlet and registering a component to it within an ngOnInit:

this.tabPortalHost = new DomPortalOutlet(
  this.tabContentOutlet.nativeElement,
  this.componentFactoryResolver,
  this.appRef,
  this.injector
);


const portal = new ComponentPortal(this.tabs[0], null, this.injector);
this.tabPortalHost.attach(portal);

By enclosing the last line in a setTimeout, the code functions as intended.

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

Angularfire/Firebase utilizes the Authorization Code Grant flow for OAuth

I am trying to understand the authentication flow used by applications that depend on Firebase for single sign-on authentication. While I know that most SPA and client applications use the "implicit flow" due to everything happening in the browser without ...

When using Angular msal_angular in conjunction with an ASP.NET Core Web API, an error may occur indicating an invalid token

I developed my Angular application using the guide provided in this example: https://github.com/microsoftgraph/msgraph-training-angularspa Successfully, I managed to log in and authenticate with MS Graph from within the Angular app. However, I am facing ...

Creating a dynamic dropdown menu triggered by a button click using Angular

I am a beginner in Angular (typescript) and I am facing some challenges in adding a new dropdown menu when a user clicks a button. My main struggle is creating additional attribute fields. I'm considering keeping track of the newly added dropdowns wit ...

How to successfully extract and understand JSON data in Angular that has been delivered from a Spring controller

I am facing a unique challenge while trying to utilize JSON data obtained from a Spring API to populate a list in Angular. Within the datasets-list.component.ts file, I have the following code: import { Component, OnInit } from '@angular/core'; i ...

What is the syntax for creating ES6 arrow functions in TypeScript?

Without a doubt, TypeScript is the way to go for JavaScript projects. Its advantages are numerous, but one of the standout features is typed variables. Arrow functions, like the one below, are also fantastic: const arFunc = ({ n, m }) => console.log(`$ ...

Accessing form validation status of page 1 in Angular2 from page 2 - A simple guide

My current project utilizes Angular 4.2.x with two main pages: page 1 containing a form and page 2 serving as a summary page. Upon successful validation of all fields on page 1, I update a boolean flag in a shared service between the two pages. The expect ...

Using multiple MaterialUI components in a JavaScript page with React can pose challenges

Incorporating multiple MaterialUI Cards in my project, I encountered an issue where all the cards would expand or the select values would change simultaneously. Despite using unique key values and mapped components with distinct keys, the problem persisted ...

Clicking the "X" close button on a mat-list removes an item from the user interface

Recently, I utilized the mat-list component to display items on the UI. Each item is accompanied by a close(X) button located on the right-hand side. https://i.stack.imgur.com/1E2ZG.png Html Code <mat-list > <ng-container *ngFor='le ...

Tips for avoiding Google Tag Manager from interfering with document.write() function

We have integrated Angular into our website, however, not all pages have been migrated to Angular yet. To handle this situation, we have implemented a hybrid approach: Each request is initially directed to Angular. Once the page is loaded, it checks if th ...

Utilizing TypeScript to reference keys from one interface within another interface

I have two different interfaces with optional keys, Obj1 and Obj2, each having unique values: interface Obj1 { a?: string b?: string c?: number } interface Obj2 { a: boolean b: string c: number } In my scenario, Obj1 is used as an argument ...

Exploring the process of enabling full text search on a multi-page encrypted document

I am faced with the task of managing hundreds of documents, each consisting of multiple pages. Users need the ability to search within a document for specific words or sentences. My goal is to retrieve all files that contain the searched text. Currently, ...

What are some ways to leverage a promise-returning callback function?

Here is a function that I have: export const paramsFactory = (params: paramsType) => { return ... } In a different component, the same function also contains await getPageInfo({ page: 1 }) after the return .... To make this work, I need to pass a cal ...

You can only use a parameter initializer within the implementation of a function or constructor

I recently started learning TypeScript and am currently using it for React Bricks. I've been working on rendering a 3D object with three.js, but I keep encountering the error mentioned above. I've attempted various solutions such as passing color ...

Dealing with Angular State Management Across Components (Direct Dependency): encountering a NullInjectorError - R3InjectorError

I have encountered a NullInjectorError in my Angular application and I am seeking assistance in resolving it. To provide context, my application consists of three main components: ProductRegistrationAndListingScreen, ProductList, and ProductRegistration. ...

Transforming an array of elements into an object holding those elements

I really want to accomplish something similar to this: type Bar = { title: string; data: any; } const myBars: Bar[] = [ { title: "goodbye", data: 2, }, { title: "universe", data: "foo" } ]; funct ...

Encountering the error "fn.dataTable.moment is not a function" when trying to use momentjs with DataTables in Angular

Feeling a bit frustrated as I struggle with what seems like a simple issue to fix. Using a DataTable that is working fine, but now I want to make a column of dates sortable in the German date format dd.MM.yyyy. Found the documentation on the official dat ...

Table pagination in Mat is experiencing overflow, and the button styles have also been customized for a unique look

I implemented a mat paginator feature, however, I am facing an issue where the alignment of items per page options is not correct. Below is the snippet from component.html file: <div class="table-responsive" *ngIf="resultssummaries"> &l ...

Tips for passing parameters from an anchor click event in TypeScript

Is it possible to send parameters to a click event from an anchor element, or should we not pass params at all? Here is the function I am using: const slideShow = (e: React.MouseEvent<HTMLAnchorElement> | undefined): void => { console.lo ...

What is the process for implementing a new control value accessor?

I have a directive that already implements the ControlValueAccessor interface (directive's selector is input[type=date]) and now I need another directive that also implements ControlValueAccessor with the selector input[type=date][datepicker] - let&ap ...

How can errors be captured when working with chained rxjs observables in an combineLatest scenario?

After reading through this thread, I encountered the following scenario: Observable.combineLatest( this.translate.get("key1"), this.translate.get(""), this.translate.get("key3"), this.translate.get("key4") ) .subscr ...