Angular 4: How to watch an object for changes?

后端 未结 6 801
死守一世寂寞
死守一世寂寞 2021-01-30 20:45

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 change

相关标签:
6条回答
  • 2021-01-30 21:22

    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/
    0 讨论(0)
  • 2021-01-30 21:29

    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

    0 讨论(0)
  • 2021-01-30 21:32

    visit https://github.com/cartant/rxjs-observe. it bases on rxjs and proxy.

    import { observe } from "rxjs-observe";
    
    const instance = { name: "Alice" };
    const { observables, proxy } = observe(instance);
    observables.name.subscribe(value => console.log(name));
    proxy.name = "Bob";
    
    0 讨论(0)
  • 2021-01-30 21:33

    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)
      })
    }
    
    0 讨论(0)
  • 2021-01-30 21:37

    We are tasked with converting an Angular 1.x app to Angular 9. It's an application with ESRI maps, so we have some neat tools that the ESRI framework brought to the table. ESRI has watchUtils that do a whole lot more than just watch for changes.

    But I missed Angular 1's simple $watch. Besides, we create Entities and Models in our application, and we may need to observe these from time to time.

    I created an abstract class called MappedPropertyClass. It uses a Map<string, any> to map class properties, which allows me to easily implement toJSON and other utility functions.

    The other Map this class has is _propertyChangeMap: Map<string, EventEmitter<{newvalue,oldvalue}>;

    We also have a function called... $watch, which takes a string and a callback function.

    This class can be extended by Entities as well as components or services

    I'm happy to share, the caveat is your properties must look like this:

    public get foo(): string {
        return this._get("foo");
    }  
    public set foo(value:string) {
        this._set("foo", value);
    }
    
    
    --------------------------------------------------------------------
    import { EventEmitter } from '@angular/core';
    
    export abstract class MappedPropertyClass {
        private _properties: Map<string, any>;
        private _propertyChangeMap: Map<string, EventEmitter<{ newvalue, oldvalue }>>;
    
        protected _set(propertyName: string, propertyValue: any) {
            let oldValue = this._get(propertyName);
            this._properties.set(propertyName, propertyValue);
            this.getPropertyChangeEmitter(propertyName).emit({ newvalue: 
        propertyValue, oldvalue: oldValue });
        }
    
        protected _get(propertyName: string): any {
            if (!this._properties.has(propertyName)) {
                this._properties.set(propertyName, undefined);
            } 
            return this._properties.get(propertyName);
        }
    
        protected get properties(): Map<string, any> {
            var props = new Map<string, any>();
            for (let key of this._properties.keys()) {
                props.set(key, this._properties.get(key));
            }
    
            return props;
        }
    
        protected constructor() {
            this._properties = new Map<string, any>();
            this._propertyChangeMap = new Map<string, EventEmitter<{ newvalue: any, 
            oldvalue: any }>>();
        }
    
        private getPropertyChangeEmitter(propertyName: string): EventEmitter<{ 
                             newvalue, oldvalue }> {
            if (!this._propertyChangeMap.has(propertyName)) {
                this._propertyChangeMap.set(propertyName, new EventEmitter<{ newvalue, 
                oldvalue }>());
            }
            return this._propertyChangeMap.get(propertyName);
        }
    
        public $watch(propertyName: string, callback: (newvalue, oldvalue) => void): 
        any {
            return this.getPropertyChangeEmitter(propertyName).subscribe((results) => 
            {
                callback(results.newvalue, results.oldvalue);
            });
        }
    }
    
    0 讨论(0)
  • 2021-01-30 21:41

    You could use custom setters to trigger your callback:

    class Customer {
      private _firstName: string
      get firstName(): string {
        return this._firstName
      }
      set firstName(firstName: string) {
        this.valueChanged(this._firstName, firstName)
        this._firstName = firstName
      }
    
      private _lastName: string
      get lastName(): string {
        return this._lastName
      }
      set lastName(lastName: string) {
        this.valueChanged(this._lastName, lastName)
        this._lastName = lastName
      }
    
      valueChanged: (oldVal, newVal) => void
    
      constructor (valueChanged?: (oldVal, newVal) => void) {
        // return an empty function if no callback was provided in case you don't need
        // one or want to assign it later
        this.valueChanged = valueChanged || (() => {})
      }
    }
    

    Then just assign the callback when you create the object:

    this.customer = new Customer((oldVal, newVal) => console.log(oldVal, newVal))
    
    // or
    
    this.customer = new Customer()
    this.customer.valueChanged = (oldVal, newVal) => console.log(oldVal, newVal)
    
    0 讨论(0)
提交回复
热议问题