Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems in communication between parent and child - Angular 6

Tags:

angular

rxjs

I'm trying to act on a parent component based on a child output, but i'm getting

Expression has changed after it was checked. Previous value: 'ngIf: [object Object]'. Current value: 'ngIf: true'.

Here is my code:

Parent Component html

<div *ngIf="hasErrors">!</div>
<app-error [valuesForErrors]="..." (hasErrors)="setHasErrors($event)"></app-error>

Parent Component ts (section)

export class InputComponent implements OnInit {
    hasErrors = false;
    ...
    setHasErrors(hasErrors) {
        this.hasErrors = hasErrors;
    }

I've seen some solutions with Subject and Observable from rxjs, but they didn't work on my project :/

Edit

A friend helped gave me another solution, just make the emitter asynchronous with a true on the declaration:

Child Component ts

export class ErrorComponent implements OnInit, OnChanges {
    @Input() valuesForErrors: any;
    @Output() hasErrors: EventEmitter<boolean> = new EventEmitter<boolean>(true);
    ...
    ngOnChanges(changes: SimpleChanges): void {
        this.calculateErrors()
    }

    calculateErrors() {
        let errors = ... some logic that returns a boolean;
        this.hasErrors.emit(errors);
    }
}
like image 896
Daniel Quicazán Avatar asked Jan 22 '26 06:01

Daniel Quicazán


2 Answers

Have you ever wondered if the world is just out to get you?

Well, sometimes it's true. You've encountered one of the nastiest behaviours that angular throws.

Normally, like 95% of the time, ExpressionChangedAfterItHasBeenCheckedError is basically angular telling you that you don't understand the basics of lifecycle detection: ngOnChanges vs ngOnInit vs ngAfterViewInit. But occasionally it's because of the nesting and template hierarchy and the order checks are made in your template.

Take the following which throws the error:

<form [name]="formName"
      [formGroup]="formGroup">
  <div *ngIf="true">
    <input formControlName="name"
           required
           placeholder="Please enter you name">
  </div>
</form>
<div *ngIf="formGroup.valid">
  Name is required.
</div>

The reason for the error being thrown is that change detection evaluates the *ngIf of the error div first before it evaluates the input's validity, as the input is one level lower in terms of template hierarchy.

A workaround is to bring the error div onto the same level as the input by adding just another <div *ngIf="true"></div> around it (as mentioned above).

Not all heros wear capes, some leave Github comments

like image 107
Andrew Allen Avatar answered Jan 25 '26 00:01

Andrew Allen


Modify parent component like this:

import { ChangeDetectorRef } from '@angular/core';

constructor(private cd: ChangeDetectorRef) {  }


setHasErrors(hasErrors) {
    this.hasErrors = hasErrors;
    this.cd.detectChanges();
}
like image 36
Adrita Sharma Avatar answered Jan 25 '26 01:01

Adrita Sharma