Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to listen for value changes from class property TypeScript - Angular

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

like image 800
L Y E S - C H I O U K H Avatar asked Mar 29 '18 09:03

L Y E S - C H I O U K H


2 Answers

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.

like image 90
Pengyy Avatar answered Sep 18 '22 15:09

Pengyy


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 
like image 28
Titian Cernicova-Dragomir Avatar answered Sep 19 '22 15:09

Titian Cernicova-Dragomir