Angular 4: How to watch an object for changes?

ETA: I know that there are various ways to watch my form for changes. That is not what I am trying to do. As the title says, I am asking how to watch for changes to an object. The app shown below is for illustration purposes only. Please answer the question that I have asked. Thanks!

I have this simple app:

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

export class Customer {
    firstName: string;
    favoriteColor: string;
}

@Component({
    selector: 'my-app',
    template: `
        <div *ngIf="customer">
            <input type="text" [(ngModel)]="customer.firstName">
            <input type="text" [(ngModel)]="customer.favoriteColor">
        </div>
        `
})
export class AppComponent implements OnInit {

    private customer: Customer;

    ngOnInit(): void {

        this.customer = new Customer();

        // TODO: how can I register a callback that will run whenever
        // any property of this.customer has been changed?

    }

}

Note the TODO. I need to register a callback that will run whenever any property of this.customer has been changed.

I cannot use ngChange on the inputs. I need to subscribe directly to changes on the model. The reasons pertain to my use case, and aren't worth going into here. Just trust me that this isn't an option.

Is this possible? I've done a lot of Googling, but I've come up dry.


Angular usually uses injected into constructor KeyValueDiffers class.

For your case it could look like:

import { KeyValueChanges, KeyValueDiffer, KeyValueDiffers } from '@angular/core';

export class Customer {
  firstName: string;
  favoriteColor: string;
}

@Component({
  selector: 'my-app',
  templateUrl: `./app.component.html`
})
export class AppComponent {
  private customerDiffer: KeyValueDiffer<string, any>;
  private customer: Customer;

  constructor(private differs: KeyValueDiffers) {}

  ngOnInit(): void {
    this.customer = new Customer();
    this.customerDiffer = this.differs.find(this.customer).create();
  }

  customerChanged(changes: KeyValueChanges<string, any>) {
    console.log('changes');
    /* If you want to see details then use
      changes.forEachRemovedItem((record) => ...);
      changes.forEachAddedItem((record) => ...);
      changes.forEachChangedItem((record) => ...);
    */
  }

  ngDoCheck(): void {
      const changes = this.customerDiffer.diff(this.customer);
      if (changes) {
        this.customerChanged(changes);
      }
  }
}

Stackblitz Example

One more option is using setter on properties that you want to check.

See also

  • http://blog.mgechev.com/2017/11/14/angular-iterablediffer-keyvaluediffer-custom-differ-track-by-fn-performance/

I need to subscribe directly to changes on the model.

Then you need to listen to model changes with ngModelChange

Template:

<input type="text" (ngModelChange)="doSomething($event)" [ngModel]="customer.firstName">

Class:

doSomething(event) {
  console.log(event); // logs model value
}

DEMO


You can't watch changes in an object. Its not angular 1 there are no watchers here. Another solution will be via observables.

use form

<form #f="ngForm">
  <input type="text" name="firstName" [(ngModel)]="customer.firstName">
  <input type="text" name="favoriteColor" [(ngModel)]="customer.favoriteColor">
</form>

in code

@ViewChild('f') f;

ngAfterViewInit() {
  this.f.form.valueChanges.subscribe((change) => {
   console.log(change)
  })
}