angularinputparent-childngonchanges

Angular: change detection triggers only once when changing value immediately in parent component


I am setting a value from the parent component and changing it immediately. In the child component, change detection executes only once.

Stackblitz code base

Parent's code: HTML

<h1>Parent Component</h1>
<p>Contents of the parent component</p>
<button (click)='parentClickEvt()'>Parent Button</button>
<child [message]="msg" (informParent)="parentWillTakeAction($event)"></child>

Parent's code: TS

export class ParentComponent {

  msg: string="testing";

  parentWillTakeAction(message) {
    this.messageFromChild = message;
  }

  parentClickEvt() {
    this.msg = "Message from parent to child";
    this.msg = "India";
    this.msg = "India, ASIA";
    this.msg = "JAPAN";
    this.msg = "JAPAN, ASIA";
  }
}

Child's code: TS

export class ChildComponent implements OnInit {

  @Input() message: string;
  @Output() informParent = new EventEmitter();

  ngOnChanges(simpleChanges: SimpleChanges) {
    console.log(simpleChanges.message.currentValue)
  }
}

Executing parentClickEvt method from parent is changing msg value 4 times. In child component, ngOnChanges is supposed to execute 4 times. But it is only executing once with the latest value.

Please suggest how ngOnChanges should execute according to all changes


Solution

  • First implement OnChanges on child component.

    Change detection is not working because you are changing value of msg without letting previous change detection to be completed.

    I would suggest to change the approach of this rapid reassignment of values if possible, However I have a workaround. see if you can use it.

    import { Component, ChangeDetectorRef } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css'],
    })
    export class AppComponent {
      messageFromChild: string = '';
      msg: string = 'testing';
    
      constructor(private changeDetectorRef: ChangeDetectorRef) {}
    
      parentWillTakeAction(message: any) {
        this.messageFromChild = message;
      }
    
      async parentClickEvt() {
        this.msg = 'Message from parent to child';
        await this.detectChanges();
        this.msg = 'India';
        await this.detectChanges();
        this.msg = 'India, ASIA';
        await this.detectChanges();
        this.msg = 'JAPAN';
        await this.detectChanges();
        this.msg = 'JAPAN, ASIA';
      }
    
      detectChanges(): Promise<void> {
        return new Promise((resolve) => {
          this.changeDetectorRef.detectChanges();
          resolve();
        });
      }
    }
    
    

    By this way you will be able to catch change in ngOnChanges of child component.