Search code examples
javascripttypescriptangular6angular-serviceskey-value-observing

Angular 2+ detect object property change inside service


Let's say we have a service like this one:

SupermanService {
  private _superman: Hero;
  public supermanReplaced = new EventEmitter<Hero>();
  public supermanPropertyUpdated = new EventEmitter<Hero>();

  public get superman(): Hero {
    return this._superman;
  }

  public set superman(superman): void {
    this._superman = superman;
    this.supermanReplaced.emit(superman);
  }

  public updateSupermanProperty(key: string, val: string | number): Hero {
    this._superman[key] = val;
    this.supermanPropertyUpdated.emit(superman);
    return this._superman;
  }

}

Is there some way to detect supermanPropertyUpdated without using the updateSupermanProperty() function but by e.g. setting this.superman.power = 10?

I found some posts that suggest the KeyValueDiffer in combination with the DoCheck hook, but that is not available for services.


Solution

  • You can use get/set methods.

    In your example:

    class SupermanService {
      private _superman: Hero;
      public supermanReplaced = new EventEmitter<Hero>();
      public supermanPropertyUpdated = new EventEmitter<Hero>();
    
      public set power(level: integer) {
        this._superman.power = level;
        this._supermanPropertyUpdated.emit(this._superman);
      }
    
      public get superman(): Hero {
        return this._superman;
      }
    
      public set superman(superman: Hero): void {
        this._superman = superman;
        this.supermanReplaced.emit(superman);
      }
    
      public updateSupermanProperty(key: string, val: string | number): Hero {
        this._superman[key] = val;
        this._supermanPropertyUpdated.emit(superman);
        return this._superman;
      }
    
    }
    

    After this you can use:

    SupermanService.power = 10;
    

    and all the listeners will be notified

    Update

    Another implementation to solve this problem is modifying your Hero class adding a public EventEmitter property and subscribing to this from your service. Assign a setter for each property on your Hero class and emit the change like Output and in your service you can emit the changes.

    class Hero {
    
        public onPropertyChange = new EventEmitter<Hero>();
        private _name: string;
    
        get name(): string {
            return this._name;
        }
    
        set name(value: string) {
            this._name = value;
            this.onPropertyChange.emit(this);
        }
    }
    
    class SupermanService {
      private _superman: Hero;
      public supermanReplaced = new EventEmitter<Hero>();
    
      public get superman(): Hero {
        return this._superman;
      }
    
      public set superman(superman: Hero): void {
        this._superman = superman;
        this.supermanReplaced.emit(superman);
      }
    
      public get supermanPropertyUpdated(): EventEmitter<Hero> {
        return this._superman.onPropertyChange;
      }
    }