7

Q) If I've got an object with loads of properties, all bound to fields in a form, how do I catch when the object has changed?

I don't want to put (blur) events on every field as the page is quite heavy already and this could lead to way too many listeners on the page.

E.g

Object:

var person = {
    name: string,
    email: string,
    phone: string
};

Form:

<input [(ngModel)]="person.name" type="text" />
<input [(ngModel)]="person.email" type="text" />
<input [(ngModel)]="person.phone" type="text" />
sebaferreras
  • 44,206
  • 11
  • 116
  • 134
Dave
  • 5,283
  • 7
  • 44
  • 66

2 Answers2

11

However, ideally I need another way, like the angular 1 $watch, as there are other ways my complex object can be changed, not just simple input fields

I was working in a Google Autocomplete Component, and I was dealing with a similar issue: when the user types an address and select one from the Google suggestions, I needed to update some other fields (like city, province, zip code, and so on).

Like @Günter Zöchbauer says, I've created an observable in order to know when something has changed in my autocomplete component, but the second problem was that the view was not being updated when that happened. That's because something very interesting and powerfull called Zones. If the concept is new for you, please refer to here and here for a great explanation.

As you can read there,

Application state change is caused by three things:

  1. Events - User events like click, change, input, submit, …

  2. XMLHttpRequests - E.g. when fetching data from a remote service

  3. Timers - setTimeout(),setInterval(), because JavaScript

… it turns out that these are the only cases when Angular is actually interested in updating the view.

So if

there are other ways my complex object can be changed

You will have to let Angular know that something has changed and needs to we aware of updating things. This is what I did:

import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class AutocompleteService {

    private autocompleteObserver: any;
    public autocomplete: any;

    constructor(...) {
        this.autocompleteObserver = null;

        this.autocomplete = Observable.create(observer => {
            this.autocompleteObserver = observer;
        });
    }

    public initializeAutocomplete(element): void { 

        // Where all the magic happens
        // ...

        // Send informtaion back to the caller
        this.autocompleteObserver.next(addressInformation);
    }

And then in my page .ts:

import { Component, NgZone } from '@angular/core';
import { AutocompleteService } from '../../providers/autocomplete-service/autocomplete-service';

@Component({
  templateUrl: 'build/pages/my-new-page/my-new-page.html',
  directives: [FORM_DIRECTIVES],
  providers: [AutocompleteService]
})
export class MyNewPage {

    constructor(..., private autocompleteService : AutocompleteService) {
    
        // Initialize all the things you need
        // ... 

       this.autocompleteService.autocomplete.subscribe((addressInfo) => {
            this.ngZone.run(() => {
                // Update the fields of the form, and Angular will update
                // the view for you.
                this.updateAddress(addressInfo);
            });
        });
    }
}

So by executing some code inside an angular zone you're telling Angular that it needs to be aware of those changes because things will probably need to be updated.

sebaferreras
  • 44,206
  • 11
  • 116
  • 134
  • 1
    Interesting thanks. I have already used NgZone to deal with the camera coming back with new data and having to let Angular know to update the view, so this isn't news to me. But informative! – Dave Jul 04 '16 at 09:28
  • Glad to be helpful :) Tried to include those resources in the answer so if other users that don't know nothing about _Zones_ read this, they could find those post quickly and understand the main idea. – sebaferreras Jul 04 '16 at 09:36
  • 1
    Thanks @sebaferreras It was really helpful – Matheus Abreu Feb 04 '17 at 02:10
1

you can use a form object and check if the form has changed.

I know that there have been some issues with latest release of Angular2 and Ionic2 with the new Forms module, but that would be my suggestion.

https://angular.io/docs/ts/latest/guide/forms.html

Aaron Saunders
  • 33,180
  • 5
  • 60
  • 80
  • Thanks, that's a good suggestion. However, ideally I need another way, like the angular 1 $watch, as there are other ways my complex object can be changed, not just simple input fields. – Dave Jul 03 '16 at 23:41
  • but that is not the question you asked, you specifically stated "Forms" so I would suggest that you reframe your question to get more appropriate answers – Aaron Saunders Jul 04 '16 at 00:47
  • Angular2 change detection doesn't check the content of objects only if the object reference points to another object instance. You can use observables in your object that emit events when a property is changed or use other custom ways of checking for changes. – Günter Zöchbauer Jul 04 '16 at 04:54