Alice
Alice

Reputation: 21

Angular 9 ng-template

I have a angular template which contains a ng-template. I have tried to insert an embedded view via ngTemplateOutlet. However I always get the following error:

core.js:4061 ERROR Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '[object Object]'.

This is the part where I am trying to use ngTemplateOutlet

<ng-template [ngTemplateOutlet]="headerLogoMiniComponent?.templateRef"></ng-template>

If i remove this line, the error will fix. What should I do to solve this problem without remove the mentioned part?
I suppose that I should add a condition with *ngIf for that wait headerLogoMiniComponent to be loaded. (But I don't know how to do that)

The declaration looks like the following:

@Component({
  selector: 'mk-layout-header-logo-mini',
  template: '<ng-template #templateRef><ng-content></ng-content></ng-template>'
})

export class HeaderLogoMiniComponent {   

@ViewChild('templateRef',{static: false}) public templateRef:TemplateRef<any>; }

@ContentChild(HeaderLogoMiniComponent, {static: false}) public headerLogoMiniComponent: HeaderLogoMiniComponent;

Upvotes: 0

Views: 1146

Answers (2)

aks44
aks44

Reputation: 432

First of all This is actually a warning rather than an error.

ExpressionChangedAfterItHasBeenCheckedError is thrown when an expression in your HTML has changed after Angular has checked it. In DevMode change detection adds an additional turn after every regular change detection run, to check if the model has changed. This error is only thrown in develop mode and it is often a sign that you should consider refactoring your code, as Angular warns you that this change in your expression will not be picked up when enabling production mode..!

One of the main root cause for this type of warnings is, you are executing some code in AfterViewInit which often happens when working with ViewChild, as it is undefined until AfterViewInit is called.

Check the below link for a detailed explanation and better understanding of ExpressionChangedAfterItHasBeenCheckedError..

ExpressionChangedAfterItHasBeenCheckedError Explained

Upvotes: 1

Beller
Beller

Reputation: 888

Probably you have to check the Angular lifecycle hooks.

  1. ngOnChanges()

Called before ngOnInit() and whenever one or more data-bound input properties change.

Respond when Angular sets or resets data-bound input properties. The method receives a SimpleChanges object of current and previous property values. Note that this happens very frequently, so any operation you perform here impacts performance significantly.

  1. ngOnInit()

Called once, after the first ngOnChanges().

Initialize the directive or component after Angular first displays the data-bound properties and sets the directive or component's input properties.

  1. ngDoCheck()

Called immediately after ngOnChanges() on every change detection run, and immediately after ngOnInit() on the first run.

Detect and act upon changes that Angular can't or won't detect on its own.

  1. ngAfterContentInit()

Called once after the first ngDoCheck().

Respond after Angular projects external content into the component's view, or into the view that a directive is in.

  1. ngAfterContentChecked()

Called after ngAfterContentInit() and every subsequent ngDoCheck().

Respond after Angular checks the content projected into the directive or component.

  1. ngAfterViewInit()

Called once after the first ngAfterContentChecked().

Respond after Angular initializes the component's views and child views, or the view that contains the directive.

  1. ngAfterViewChecked()

Called after the ngAfterViewInit() and every subsequent ngAfterContentChecked().

Respond after Angular checks the component's views and child views, or the view that contains the directive.

  1. ngOnDestroy()

Called immediately before Angular destroys the directive or component.

Cleanup just before Angular destroys the directive or component. Unsubscribe Observables and detach event handlers to avoid memory leaks.

Visually something like this:

enter image description here

So you should put ViewChild assignment in ngAfterViewInit() and the ContentChild assignment you should put in ngAfterContentInit() lifecycle hook.

ngAfterContentInit() {
    this.headerLogoMiniComponent = 'something';
}

ngAfterViewInit() {
    this.templateRef = 'something';
}

Upvotes: 1

Related Questions