OnChanges does not trigger on partial object change

2.6k Views Asked by At

I am having issues while triggerin Angular(6)'s onChanges life cycle hooks. While emitting parameters from a compontent to a directive I want to hook on the changes.

The trigger works perfectly on one-dimensional variables, while objects are not triggered correctly. Especially if an partial of an multi-dimensional object is transmitted to a child directive. At least it works, but the OnChange hook only triggers the one-dimensional variable.

Have a closer look on StackBlitz, where I've provided an executable example. Mind to open the console, to see, which life cycle is triggered: https://stackblitz.com/edit/angular-vhaews

app.component.ts

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  public paramNumber;
  public paramObject;

  ngOnInit() {
    this.paramObject = {
      x: 10,
      y: 10
    };
    this.paramNumber = 42;
  }
}

app.component.html

<div appCanvas [paramObject]="paramObject" [paramNumber]="paramNumber"></div>

ParamObject X
<input name="paramObject.x" type="number" [(ngModel)]="paramObject.x"/>
<br>
ParamObject Y
<input name="paramObject.y" type="number" [(ngModel)]="paramObject.y"/>
<pre>{{paramObject | json}}</pre>

ParamNumber
<input name="paramNumber" type="number" [(ngModel)]="paramNumber"/>
<pre>{{paramNumber | json}}</pre>

canvas.directive.ts

import { Directive, ElementRef, Input, OnInit, OnChanges } from '@angular/core';

@Directive({
  selector: '[appCanvas]'
})
export class CanvasDirective implements OnInit, OnChanges {

  @Input() private paramObject;
  @Input() private paramNumber;

  ngOnChanges() {
    console.log(`changed object`, this.paramObject);
    console.log(`changed number`, this.paramNumber);
  }

  ngOnInit() {
    console.log(`changed object`, this.paramObject);
    console.log(`changed number`, this.paramNumber);
  }
}

Disclaimer: That problem is part of an bigger issue and I've tried to isolate the main issue. So please do not look to much into details, it's really about the life cycle hook and the multi-dimensional object.

2

There are 2 best solutions below

0
On

Wrapping all suggestions up, it looks like splitting the object properties into individual variables is the best solution:

app.component.html

<input name="canvasParam.x" type="number" [(ngModel)]="canvasParam.x"/>

canvas.directive.ts

private param;

@Input() private paramX;
@Input() private paramY;


ngOnChanges() {
  this.param = {
    x: this.paramX,
    y: this.paramY
  };
  console.log(`changes`, this.param);
}
4
On

This is a typical angular scenario when detecting changes in an Object's properties. Take a look at ngDoCheck(), which can be used to put custom change detection logic.

Angular lifecycle will only detect the object reference changes. Because why not? Doing a deep object equals comparison can be a performance hit!

What you can do is -

  1. Replace the object with a new instance. For instance, use Object.assign.
  2. Put custom change detection logic in ngDoCheck() method.