Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular2 watch object/array changes (Angular2 final >= 2.1.1)

I'd like to watch an object/array, which ca be edited by a service or by a controllers routine. I thought that an Observable could watch an object/array.

My implementation doesn't react on changes of the items :

  private data : Observable<Array<any>>;
  private dataObserver: Observer<Array<any>>;
  private sub : Subscription;
  private items: <Array<any>>;

  ngOnInit() {
     this.items = itemService.getItems();
     this.data = new Observable<Array<any>>(observer =>{
        this.dataObserver = observer;
     });
     this.data.subscribe(
        x => console.log('onNext: %s', x),
        e => console.log('onError: %s', e),
        () => console.log('onCompleted')
     );
     this.dataObserver.next(this.items);
  }


private start(){

  //change values of the array in an interval
  let loop = Observable.interval(250)
  let i=0;
  self.sub = loop.subscribe(() => {
      if(self.items[0]){
        self.items[0].id= i;
        if(i<100) i++;
        else i=1;
      }
  })
}

The observalbes subsciption doesn't react on changes of the items array. It only triggers on its next mehtod. On the other hand .. this is too cumbersome for a simple watch method.

What does angular-2 offer us to watch for changes, as $scope.$watch did in angular-1?

like image 986
marcel Avatar asked Nov 24 '16 10:11

marcel


3 Answers

In the import section:

import { DoCheck, KeyValueDiffers, KeyValueChangeRecord } from '@angular/core';

Adding the 'KeyValueDiffers' injector:

private _differ: any;    

constructor(private _differs: KeyValueDiffers) {
    this._differ = _differs.find({}).create();
}

Finally track changes:

ngDoCheck() {
    const change = this._differ.diff(this.Your_Object_To_Track);
    if (change) {
        change.forEachChangedItem(
            (record: KeyValueChangeRecord<any, any>) => {
                console.log(record.key + ': ' +  record.previousValue + '=>' + record.currentValue) });

        change.forEachRemovedItem(                
                (record: KeyValueChangeRecord<any, any>) => {
                    console.log(record.key + ': ' +  record.previousValue + '=>' + record.currentValue) });

        change.forEachAddedItem((record: KeyValueChangeRecord<any, any>) => {
                console.log(record.key + ': ' +  record.previousValue + '=>' + record.currentValue) });
    }
}
like image 73
Hany Avatar answered Nov 12 '22 05:11

Hany


Angular2 provides IterableDiffer (array) and KeyValueDiffer (object) to get information about differences between two checks.

NgClass is a good example https://github.com/angular/angular/blob/14ee75924b6ae770115f7f260d720efa8bfb576a/modules/%40angular/common/src/directives/ng_class.ts#L122

See also https://angular.io/docs/ts/latest/api/#!?query=differ

An example

// inject a differ implementation 
constructor(differs: KeyValueDiffers) {
  // store the initial value to compare with
  this.differ = differs.find({}).create(null);
}

@Input() data: any;

ngDoCheck() {
  var changes = this.differ.diff(this.data); // check for changes
  if (changes && this.initialized) {
    // do something if changes were found
  }
}
like image 14
Günter Zöchbauer Avatar answered Nov 12 '22 05:11

Günter Zöchbauer


Thanks for your example Gunter.

I choose a quick solution based on your response.

I replace the OnInit handle by a DoCheck implementation. Now if value change in my service external called Language the view is updated.

In my case example :

import { Component, DoCheck } from "@angular/core";
export class LangListUserComponent implements DoCheck {

  constructor(private _languageService: LanguageService)
  {}

  ngDoCheck() {
    /** Get available lang */
    this.oLanguages = this._languageService.getLanguageList();
    this.setCurrentLang(this.oLanguages);
  };
}
like image 1
TGA Avatar answered Nov 12 '22 06:11

TGA