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

Executing a cloud function in Firebase from an Angular-Ionic application by making an HTTP request

I am a newcomer to GCP and app development, so please bear with me if this question seems mundane. Currently, I have an angular-ionic app that is connected to Firebase allowing me to interact with the Firestore database. Now, my challenge is to invoke a ht ...

Android encountered an unknown error while trying to access the URL, resulting in an HTTP failure response with a status

My Ionic app runs perfectly fine on iOS, but when I try to run it on Android, I encounter the following error: Http failure response for (unknown url): 0 Unknown Error https://i.stack.imgur.com/8EFna.png I have CORS enabled on the server side and it wor ...

Enhance your spreadsheet by incorporating dynamic columns utilizing xlsx and sheetjs libraries

I have an array consisting of multiple tags with unique ids and corresponding data: [ { "id": "tagID1", "error": { "code": 0, "success": true }, "data": [ [1604395417575, 108 ...

Angular array sanitization for handling multiple URLs

I need to sanitize multiple URLs from an array containing links to video sites e.g.: videos: SafeResourceUrl = ['www.someURL1', 'www.someURL2',... ]; To achieve this, I created a constructor like so: constructor(private sanitizer ...

Tips for utilizing the polymorphic feature in TypeScript?

One of the challenges I am facing involves adding data to local storage using a function: add(type: "point" | "object", body: FavouritesBodyPoint | FavouritesBodyObject) { // TODO } export interface FavouritesBodyPoint {} export in ...

NodeJS Jest test failing due to a global variable being set for a different test already

I am currently working on a project in TypeScript using Node.js and Jest. I have a function that may or may not modify a global variable, which is a TypeScript Map. After one test modifies the global variable, it retains that value for subsequent tests. I ...

Error messages are being generated by Angular CLI due to an unclosed string causing issues with the

After incorporating styles from a Bootstrap theme into my Angular 8 project and updating angular.json, I encountered a compile error displaying the following message: (7733:13) Unclosed string 7731 | } 7732 | .accordion_wrapper .panel .panel-heading ...

The router.navigate() function seems to be malfunctioning as it is not working as

I have a method defined as follows: private redirect(path: string): void { this.router.navigate([path]); } This method is called within another method like so: private onError(error: any): void { switch (error.status) { case 401: / ...

The dispatch function of useReducer does not get triggered within a callback function

Can anyone assist me with this issue I am facing while using React's useReducer? I'm trying to implement a search functionality for items in a list by setting up a global state with a context: Context const defaultContext = [itemsInitialState, ...

Transform a base64 image into a blob format for transmission to the backend via a form

Is there a way to convert a base64 string image to a blob image in order to send it to the backend using a form? I've tried some solutions like this one, but they didn't work for me. function b64toBlob(b64Data, contentType='', sliceSiz ...

Develop an Angular 5 application that incorporates server-side rendering through Angular Universal and deploy it on Google App

As a newcomer to node.js and angular, I have put together a simple Angular 5 app with basic routes. Now, I am looking to implement server-side rendering using Angular Universal and host my application on Google Cloud App Engine. I attempted to upload an A ...

Setting up React Context API with TypeScript: A Step-by-Step Guide

I recently updated my app.js file to app.tsx for improved functionality. However, I encountered an error with the current value. app.tsx import React, { createContext, useState } from "react"; import SelectPage from "./pages/select/select& ...

Verify enum values within controller function

I am dealing with a query parameter in my REST API that should be restricted to specific values according to an enum type. I need to find a way to handle a "Bad Request" error if the client provides any value outside of this enum. Here is what my enum loo ...

Encountering difficulties when trying to deploy Angular 4 with TypeScript on Heroku

I have been facing an issue while trying to deploy my angular 4 app on Heroku. The deployment fails to proceed properly even though everything works fine locally. Here are the relevant files: package.json { "name": "webcli2", "version": "0.0.0", " ...

Access specific files within a workspace in VS Code with read-only permissions

Currently, I am engaged in a typescript project within Visual Studio Code. In my workflow, a gulp task is responsible for transferring code to a designated folder. The files copied will be utilized by corresponding files located in the destination folder t ...

Encountering difficulties with implementing reactive forms in an Ionic Angular 7 project as the app.module.ts file appears to be missing

Currently, I am working on a project using Ionic Angular 7 and I am facing some challenges with implementing reactive forms. Since app.module.ts is not in Ionic Angular 7 anymore, I tried to search for solutions online. Unfortunately, when I followed the i ...

Obtaining the accurate return type based on the generic parameter in a generic function

Is there a way to determine the correct return type of a function that depends on a generic argument? function f1<T>(o: T) { return { a: o } } // How can we set T to number through (n: number)? type T1 = typeof f1 extends (n: number) => infe ...

The response error from the callback of the HTTP service in CouchDB with AngularJS lacks any meaningful data

I need help writing CouchDB TypeScript classes with AngularJS http service. My call is successful in the happy path, but I'm struggling to retrieve the status code or JSON error information from the server, which is crucial for user feedback. public ...

Tips for identifying two mat-tables within the same component in Angular UI

Having trouble with rendering rows in two mat-tables within the same component. The renderRows() function is not working for the second table. @ViewChild(MatTable) tagsTable: any; @ViewChild(MatTable) serviceAreaTable:any; this.tagsTable.renderRows(); # ...

Transforming a string into key-value pairs using Typescript

Is there a way to transform the given string into key-value pairs? TotalCount:100,PageSize:10,CurrentPage:1,TotalPages:10,HasNext:true,HasPrevious:false ...