AkiZukiLenn
AkiZukiLenn

Reputation: 388

How to fix Angular Error: NG0100 before ngOnChange is called

There are 2 child-components (bar/edit) under app-component, edit component will emit an @output number from ngOnChanges function to the app component . Which the app component will stored it like below.

app.component.ts

editCount:number=0;
editCountReceiver(data:number){
  this.editCount=data;
  console.log("app component receieved = "+this.editCount);
} 

app.component.html

<app-edit (send_edit_count)="editCountReceiver($event)"></app-edit>

The bar component will then get the number from the app component by using @input like below .

app.component.html

<app-search-bar [total_edit]="editCount"">

bar.component.typescript

@Input() total_edit: number  ; 

And then the error appeared.
"ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '0'. Current value: '1'.. "

One line of code is included in the error message, as shown below.

<app-search-bar [total_edit]="editCount"">

I was trying to fix this issue for couple of hours already and I have found out it has to do with the hook thing. So I decided to console.log almost all hooks for further investigation. The result is quite long so I just capture few lines that I think might be helpful.

enter image description here

Supposedly, since the value is bind with @input. I expect the ngOnChanges is called right after app component receive number and update bar component @input number accordingly.

But no, an error message is appeared before the ngOnChanges called. I presume it's because the @input data is updated slower than the afterViewChecked hook. So please, how can I fix this issue and make sure @input update is detected and applies before the afterViewChecked hook ?

Upvotes: 0

Views: 3407

Answers (2)

Charles Robertson
Charles Robertson

Reputation: 1820

After 2 days of tearing my hair out and searching the most obscure recesses of the internet, I have something to add, that none of the other forums mention, including SO.

I am adding this solution, because I see you are using an @Output emitter.

If you have run out of ideas and are still getting:

ERROR Error: NG0100: ExpressionChangedAfterItHasBeenCheckedError

Please check your @Output reference methods. What do I mean by reference method? Here is the pattern:

// child-component.component.ts

@Component({
    selector: 'app-child'
})
export class ChildComponent {
    @Output() someOutput = new EventEmitter<boolean>();
    ngOnInit() {
        this.someOutput.emit(true);
    }
}

// parent-component.component.ts

@Component({
    selector: 'app-parent'
})
export class ParentComponent {
    someOutput(input: boolean): void {
        setTimeout( () => {
          this.input = input;
          // do stuff
        });
    }
}

// parent-component.component.html

<app-child
  (someOutput)="someOutput($event)">
</app-child>

The key here is to add:

setTimeout( () => {
}

Inside your output reference method.

All of the other solutions seem to talk exclusively about the ExpressionChangedAfterItHasBeenCheckedError error, in relation to the @Input binding, but the @Output emitter, is just as vulnerable to this issue.

I am going to add this solution to as many relevant forum threads, as possible, because very little has been written about this notorious error, when using the @Output emitter. I apologize, in advance, if this answer isn't 100% relevant, to this question, however if this solution helps, at least one person, then I feel it will have been a worthwhile task.

Upvotes: 1

JFPicard
JFPicard

Reputation: 5168

As you describe, if you need to be notified, it's probably with a Subject that you must subscribe. Since I don't have all the code in your question, it's hard to give you a good answer.

In that case, one component can emit the value with the Subject by calling next and the component that need to be notified can Subcribe to the same Subject.

After some research, you can use a setter on the Input, but in this case, you will run after some problems if the value is passed by reference. (ex. An array, or an object).

In that case, something like this can work:

private editCountField: number;
get countField(): number{
    return this.editCountField;
}
@Input() set countField(newCountField: number) {
    this.editCountField = newCountField;
    this.doSomething();
}

Upvotes: 1

Related Questions