In AngularJS, we can listen variable change using $watch
, $digest
... which is no longer possible with the new versions of Angular (5, 6).
In Angular, this behaviour is now part of the component lifecycle.
I checked on the official documention, articles and especially on Angular 5 change detection on mutable objects, to find out how to listen to a variable (class property) change in a TypeScript class / Angular
What is proposed today is :
import { OnChanges, SimpleChanges, DoCheck } from '@angular/core'; @Component({ selector: 'my-comp', templateUrl: 'my-comp.html', styleUrls: ['my-comp.css'], inputs:['input1', 'input2'] }) export class MyClass implements OnChanges, DoCheck, OnInit{ //I can track changes for this properties @Input() input1:string; @Input() input2:string; //Properties what I want to track ! myProperty_1: boolean myProperty_2: ['A', 'B', 'C']; myProperty_3: MysObject; constructor() { } ngOnInit() { } //Solution 1 - fired when Angular detects changes to the @Input properties ngOnChanges(changes: SimpleChanges) { //Action for change } //Solution 2 - Where Angular fails to detect the changes to the input property //the DoCheck allows us to implement our custom change detection ngDoCheck() { //Action for change } }
This is only true for @Input()
property !
If I want to track changes of my component's own properties (myProperty_1
, myProperty_2
or myProperty_3
), this will not work.
Can someone help me to solve this problematic ? Preferably a solution that is compatible with Angular 5
You can still check component's field members value change by KeyValueDiffers
via DoCheck
lifehook.
import { DoCheck, KeyValueDiffers, KeyValueDiffer } from '@angular/core'; differ: KeyValueDiffer<string, any>; constructor(private differs: KeyValueDiffers) { this.differ = this.differs.find({}).create(); } ngDoCheck() { const change = this.differ.diff(this); if (change) { change.forEachChangedItem(item => { console.log('item changed', item); }); } }
see demo.
I think the nicest solution to your issue is to use a decorator that replaces the original field with a property automatically, then on the setter you can create a SimpleChanges
object similar to the one created by angular in order to use the same notification callback as for angular (alternatively you could create a different interface for these notifications, but the same principle applies)
import { OnChanges, SimpleChanges, DoCheck, SimpleChange } from '@angular/core'; function Watch() : PropertyDecorator & MethodDecorator{ function isOnChanges(val: OnChanges): val is OnChanges{ return !!(val as OnChanges).ngOnChanges } return (target : any, key: string | symbol, propDesc?: PropertyDescriptor) => { let privateKey = "_" + key.toString(); let isNotFirstChangePrivateKey = "_" + key.toString() + 'IsNotFirstChange'; propDesc = propDesc || { configurable: true, enumerable: true, }; propDesc.get = propDesc.get || (function (this: any) { return this[privateKey] }); const originalSetter = propDesc.set || (function (this: any, val: any) { this[privateKey] = val }); propDesc.set = function (this: any, val: any) { let oldValue = this[key]; if(val != oldValue) { originalSetter.call(this, val); let isNotFirstChange = this[isNotFirstChangePrivateKey]; this[isNotFirstChangePrivateKey] = true; if(isOnChanges(this)) { var changes: SimpleChanges = { [key]: new SimpleChange(oldValue, val, !isNotFirstChange) } this.ngOnChanges(changes); } } } return propDesc; } } // Usage export class MyClass implements OnChanges { //Properties what I want to track ! @Watch() myProperty_1: boolean = true @Watch() myProperty_2 = ['A', 'B', 'C']; @Watch() myProperty_3 = {}; constructor() { } ngOnChanges(changes: SimpleChanges) { console.log(changes); } } var myInatsnce = new MyClass(); // outputs original field setting with firstChange == true myInatsnce.myProperty_2 = ["F"]; // will be notified on subsequent changes with firstChange == false
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With