Home > Net >  Angular: How to react to changes in fields of child components
Angular: How to react to changes in fields of child components

Time:04-29

In my Angular app I have included a child component. This has two fields of the Boolean type. In my parent app, I want to react as soon as the values have changed.

///////////////////////////////////////////////////////
// This ist the CHILD
///////////////////////////////////////////////////////
@Component({
  selector: 'critical',
  template: `<div ><h1>This is a critical component</h1></div>`,
  styles: [`h1 { font-family: Lato; } .blueBorder {border: 1px solid blue;}`],
})
export class CriticalComponent {
  error: boolean = false;
  warning: boolean = false;
}
///////////////////////////////////////////////////////
// This ist the PARENT
///////////////////////////////////////////////////////
@Component({
  selector: 'my-app',
  template: '<critical #criticalCmp></critical>',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  @ViewChild('criticalCmp') criticalCmp: CriticalComponent;

  one rrorsOrWarningsInCriticalComponent(): void {
    // I want to run this method everytime the values in the critical-component changed to
    // call services, print logs or do other things here
  }
}

How can I achieve this?

To illustrate, I've prepared a StackBlitz here: https://stackblitz.com/edit/angular-ivy-hpn8qy?file=src/app/app.component.ts

CodePudding user response:

You can use a setter, together with an Output I guess.

Parent:

import { Component, ViewChild } from '@angular/core';
import { CriticalComponent } from './critical.component';

@Component({
  selector: 'my-app',
  template: '<critical #criticalCmp (errorChanged)="errorChanged($event)"></critical>',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  @ViewChild('criticalCmp') criticalCmp: CriticalComponent;

  one rrorsOrWarningsInCriticalComponent(): void {
    // I want to run this method everytime the values in the critical-component changed to
    // call services, print logs or do other things here
  }

  errorChanged(isError: boolean) {
    if(isError){
      this.onErrorsOrWarningsInCriticalComponent();
    }
  }
}

Child:

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'critical',
  template: `<div ><h1>This is a critical component</h1></div>`,
  styles: [`h1 { font-family: Lato; } .blueBorder {border: 1px solid blue;}`],
})
export class CriticalComponent {
  @Output() 
  errorChanged = new EventEmitter();
  set error(val: boolean) {
    this._error = val;
    this.errorChanged.next(val);
  };
  get error() {
    return this._error;
  }
  warning: boolean = false;

  private _error: boolean = false;
}

Component communication can take place in many ways, you can find good reading from the docs: https://angular.io/guide/component-interaction

CodePudding user response:

If you are interested in a reactive (Observable) approach, I would recommend the following.

Make error and warning BehaviorSubjects, give them the initial value you want:

error = new BehaviorSubject(false);
warning = new BehaviorSubject(false);

Use combineLatest together with @Output() to emit whenever values change:

@Output() myBooleans = combineLatest({
  error: this.error.asObservable(),
  warning: this.warning.asObservable(),
}).pipe(skip(1));

I'm using skip(1) to disregard the first change (if that is what you want, to not emit the initial values).

The parent listens to this output:

<critical (myBooleans)="onErrorsOrWarningsInCriticalComponent($event)"></critical>

and fires your function when something changes.

And in child, when you want to change either value you can call: this.error.next() and this.warning.next().

I also created an interface for this object, which I always would recommend, to type your data :)

Here is your forked STACKBLITZ with the above solution.

  • Related