简体   繁体   中英

ExpressionChangedAfterItHasBeenCheckedError` error angular 4

i am trying to work on a project which has multiple components. Example app is here . I am in a situation where i am getting the error in the development mode as follows

错误

I know why this error is coming

Because after the child component values are bound , parent component values are updated by grandparent component, resulting in throwing of error during angular dirty check

The skeleton of my app is here . Can someone please help me resolve this issue. I know it could be solved by using setTimeout or changeDetectionRef. But thats not a solution i am looking for, i want to know what am i doing wrong during the life cycle hooks. Thanks in advance.

Glad to hear that you are against setTimeout and manually calling detectChanges .

I know that this is obvious in your app structure but here is an example that show you where the error comes from:

https://ng-run.com/edit/d9EEfZLhGfM0fYQtehhs?open=app%2Fgrandparent%2Fparent%2Fchild%2Fchild.component.html&layout=2

Because after the child component values are bound , parent component values are updated by grandparent component, resulting in throwing of error during angular dirty check

Seems you're right your child component is rendered before ngAfterContentInit happens.

Angular application is a tree of views. When angular runs change detection mechanism it goes through this tree of view and call checkAndUpdateView function for each of views.

During execution of this function angular calls other functions in a strictly defined order. In our case we are insterested in these functions:

execEmbeddedViewsAction
callLifecycleHooksChildrenFirst (AfterContentInit)
execComponentViewsAction

It means that angular will call change detection cycle for embedded views( ng-template generates embedded view) earlier than executing ngAfterContentInit hook . This is what happens in your example:

在此处输入图片说明

As we can see in the picture above when angular checks AppComponent view it checks embedded views first and then calls ngAfterContentInit for GrandParentComponent and goes down to GrandParentComponent view.

Seems there are many ways to solve it. I tried on of them in this Demo

The key moment is not using ngAfterContentInit hook but rather set index and active values within ParentComponent :

parent.component.ts

export class ParentComponent {
  private _active: boolean = false;
  public index = 0;

  constructor(
      @Host() @Inject(forwardRef(() => GrandParentComponent))
      public grandParentComponent: GrandParentComponent) {
    this.index = this.grandParentComponent.parents.length;
    this.grandParentComponent.parents.push(this)
  }

  ngOnInit() {
    this.active = this.index == this.grandParentComponent.currentStep;
  }

  ngOnDestroy() {
    if (this.grandParentComponent) {
      this.grandParentComponent.parents = this.grandParentComponent.parents.filter(x => x !== this);
    }
  }

where parents property has been declared in GrandParentComponent :

grandparent.component.ts

export class GrandParentComponent {

  parents = [];

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM