Angular Dynamic Components and ExpressionChangedAfterItHasBeenCheckedError

Tomas Piaggio

I'm trying to write a component that could contain different components dynamically. My goal is to be able to write an article where I could either write a paragraph or add a tweet.

This is the code for DynamicArticleComponent:

@Directive({
  selector: '[dynamic-query]'
})
export class QueryDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

@Component({
  selector: 'app-dynamic-article',
  template: 
  `<ng-container *ngFor="let element of elements">
      <ng-template dynamic-query></ng-template>
  </ng-container>`,
  styleUrls: ['dynamic-article.component.css']
})
export class DynamicArticleComponent implements AfterViewInit {

    @Input() elements: Element[];
    @ViewChildren(QueryDirective) queryDirectives;

    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    ngAfterViewInit() {
      this.queryDirectives.forEach((queryDirective: QueryDirective, index) => {
        const element = this.elements[index];
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(element.component);
        const containerRef = queryDirective.viewContainerRef;
        containerRef.clear();
        const newComponent = containerRef.createComponent(componentFactory);
        (<DynamicComponent>newComponent.instance).data = element.data; 
      });
    }
}

These are other classes used in the code above:

export class Element {
    constructor(public component: Type<any>, public data) {}
}

export interface DynamicComponent {
    data: any;
}

I'm having trouble rendering the <ng-templates>. It just renders comments and it doesn't change after the view loads. This is what's rendered: enter image description here

The elements are getting to the component correctly. My idea is to render all the templates, then get them with the ViewChildren decorator, and render the elements where they are supposed to be. Is there other solution to this problem?

Also, this is how the elements reach the DynamicArticleComponent:

enter image description here

Thanks in advance.

Tomas Piaggio

Ok, there were two main problems with my code. The first one was pretty dumb. I didn't add the directive to the app module declarations, so it was just like any other html property; angular just didn't expect it, so it didn't look for it. However, after adding it to the app module, it threw ExpressionChangedAfterItHasBeenCheckedError. This error is caused because I'm changing variables after the view has load. For a more in depth explanation look this blog post.

In summary, what I did was extracting what I was doing inside the ngAfterViewInit into its own function and calling it from a promise. What this does, is creates a microtask queued after the syncronous code has fininshed executing. To learn more about micro and macro tasks in angular, look at this post: I reverse-engineered Zones (zone.js) and here is what I’ve found.

Here is how the code ended up:

@Directive({
  selector: '[dynamic-query]'
})
export class QueryDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}

@Component({
  selector: 'app-dynamic-article',
  template: 
  `<ng-container *ngFor="let element of elements">
    <ng-template dynamic-query></ng-template>
  </ng-container>`,
  styleUrls: ['dynamic-article.component.css']
})
export class DynamicArticleComponent implements AfterViewInit {

    @Input() elements: Element[];
    @ViewChildren(QueryDirective) queryDirectives;

    constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

    ngAfterViewInit() {
        Promise.resolve(null).then(() => this.renderChildren());
    }

    private renderChildren() {
      this.queryDirectives.forEach((queryDirective: QueryDirective, index) => {
        const element = this.elements[index];
        const componentFactory = this.componentFactoryResolver.resolveComponentFactory(element.component);
        const containerRef = queryDirective.viewContainerRef;
        containerRef.clear();
        const newComponent = containerRef.createComponent(componentFactory);
        (<DynamicComponent>newComponent.instance).data = element.data; 
      });
    }
}

This code totally works. Hopefully I help someone.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

ExpressionChangedAfterItHasBeenCheckedError: with two sibling Angular components

Angular dynamic component loading - ExpressionChangedAfterItHasBeenCheckedError

Angular (1.5.8) Dynamic Components

ExpressionChangedAfterItHasBeenCheckedError: angular

ExpressionChangedAfterItHasBeenCheckedError Angular

Dynamic addition and removal of components Angular

Creating nested dynamic components in angular

Problems with Dynamic components Angular 12

ExpressionChangedAfterItHasBeenCheckedError dynamic reactive forms

Angular 4 ExpressionChangedAfterItHasBeenCheckedError

ExpressionChangedAfterItHasBeenCheckedError with Angular 6 and redirect

Angular loading button ExpressionChangedAfterItHasBeenCheckedError

Angular 4.4 - ExpressionChangedAfterItHasBeenCheckedError

Ionic / Angular: Avoiding the ExpressionChangedAfterItHasBeenCheckedError

Angular TemplateRef ExpressionChangedAfterItHasBeenCheckedError

Angular - ExpressionChangedAfterItHasBeenCheckedError on ngFor

ExpressionChangedAfterItHasBeenCheckedError from Angular

Angular 7, Ngrx, ExpressionChangedAfterItHasBeenCheckedError

Angular throws ExpressionChangedAfterItHasBeenCheckedError

Error: ExpressionChangedAfterItHasBeenCheckedError varaible Angular?

ExpressionChangedAfterItHasBeenCheckedError Explained in toastr Angular

Dynamic nested reactive form: ExpressionChangedAfterItHasBeenCheckedError

Angular 2 - Why is the binding of dynamic components broken?

Angular Dynamic Components: How to Set and Get Data

How to inject providers for dynamic components in Angular?

Angular Dynamic Add rows with custom components

angular 5: interchange locations of dynamic components

Angular 2 OnPush change detection for dynamic components

Angular Circular Dependencies with Dynamic Nested Components

TOP Ranking

  1. 1

    Failed to listen on localhost:8000 (reason: Cannot assign requested address)

  2. 2

    Loopback Error: connect ECONNREFUSED 127.0.0.1:3306 (MAMP)

  3. 3

    How to import an asset in swift using Bundle.main.path() in a react-native native module

  4. 4

    pump.io port in URL

  5. 5

    Compiler error CS0246 (type or namespace not found) on using Ninject in ASP.NET vNext

  6. 6

    BigQuery - concatenate ignoring NULL

  7. 7

    ngClass error (Can't bind ngClass since it isn't a known property of div) in Angular 11.0.3

  8. 8

    ggplotly no applicable method for 'plotly_build' applied to an object of class "NULL" if statements

  9. 9

    Spring Boot JPA PostgreSQL Web App - Internal Authentication Error

  10. 10

    How to remove the extra space from right in a webview?

  11. 11

    java.lang.NullPointerException: Cannot read the array length because "<local3>" is null

  12. 12

    Jquery different data trapped from direct mousedown event and simulation via $(this).trigger('mousedown');

  13. 13

    flutter: dropdown item programmatically unselect problem

  14. 14

    How to use merge windows unallocated space into Ubuntu using GParted?

  15. 15

    Change dd-mm-yyyy date format of dataframe date column to yyyy-mm-dd

  16. 16

    Nuget add packages gives access denied errors

  17. 17

    Svchost high CPU from Microsoft.BingWeather app errors

  18. 18

    Can't pre-populate phone number and message body in SMS link on iPhones when SMS app is not running in the background

  19. 19

    12.04.3--- Dconf Editor won't show com>canonical>unity option

  20. 20

    Any way to remove trailing whitespace *FOR EDITED* lines in Eclipse [for Java]?

  21. 21

    maven-jaxb2-plugin cannot generate classes due to two declarations cause a collision in ObjectFactory class

HotTag

Archive