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:
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 = [];