Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 Dynamic content loading throws Expression changed exception

Tags:

angular

I'm creating a generic Gantt visualization component.

The part where I will display the weeks and tasks is generic, but every row will have a header that I want to be dynamic, so for example, when displaying a list of tasks per user I want to put some user data and some action buttons, but when displaying tasks by project I want to display project data and project action buttons.

I implemented a solution following this: https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html

Everything seems to work fine, I see the actions buttons load differently in each scenario, but not the user or project data. Also, console.log(this.data.name) on the button click, I correctly see the data in the console, but if I try to print in on the template {{ data.name }} I see nothing.

I see the following error in console:

Error: Expression has changed after it was checked. Previous value: 'CD_INIT_VALUE'. Current value: 'test project - Task 3 '. 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 double checked all steps, and I'm doing the exact same thing you can see on that tutorial.

Does anyone know if that tutorial is out dated?

thank you in advance for your help!

EDIT: If I change the ngAfterViewInit to ngOnInit it all works... but according to the tutorial I should be using the first. Can anyone explain to me this? :-)

like image 464
RollWhisTler Avatar asked Feb 22 '17 09:02

RollWhisTler


1 Answers

The error says it all:

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?

You've created component dynamically ("outside" of angular lifecycle), this means you need to control change detection manually.

So that is what you need to do:

1) After creating a component fire detectChanges() for it

2) Before disposing detach() change detection;

Here is example of my directive that creates component dynamically:

export class ValidationMessageDirective implements AfterViewInit, OnDestroy {

  private validationMessageComponent: ComponentRef<ValidationMessageComponent> = null;

  ngAfterViewInit(): void {
    let factory = this.componentFactoryResolver.resolveComponentFactory(this.vmComp);
    this.ngOnDestroy();
    this.validationMessageComponent = this.viewContainer.createComponent(factory, null, this.viewContainer.injector);
    this.validationMessageComponent.changeDetectorRef.detectChanges();
  }

  ngOnDestroy(): void {
    if (this.validationMessageComponent) {
      this.validationMessageComponent.changeDetectorRef.detach();
      //this.validationMessageComponent.destroy();
    }
  }

  constructor(
    private viewContainer: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver,
    @Inject(ValidationMessageComponent) private vmComp: Type<ValidationMessageComponent>
  ) { }
}

Related issue: https://github.com/angular/angular/issues/11007

like image 175
Sergey Romanchuk Avatar answered Oct 11 '22 14:10

Sergey Romanchuk