如何在 angular4+ 服务中为 setter 构建接口?

How to build an interface for setters in a angular4+ service?

我对 JavaScript 和 angular 有一些经验,但我不太了解某些概念。 我正在尝试构建一个服务来存储一些设置,这些设置能够通知实现它的组件发生了更改。

设置为每个 属性 调用 onChange 函数的设置器似乎是老式的方法,我让它工作了。但我不喜欢 angular 方式, 因为它使 class 膨胀,对于我想添加的每个 属性,我需要实现新的获取和设置方法。 所以我想有一个接口,每次 属性 被设置为新的东西时调用 onChange 函数。但是我找不到这个的语法。

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

@Injectable()
export class serviceSettings {
  private constructed: boolean = false;

  private _targetFPS:          number  = 30;
  private _showFPS:            boolean = true;
  private _viewDistance:       number  = 7000;
  private _enabelAntialiasing: boolean = true;

  private settingsChanged =   new Subject();

  constructor() {
    if (this.constructed) {
        console.log('serviceSettings aready exists');
        return this
    }
    this.constructed = true;
  }

  onChange() {
    this.settingsChanged.next();
    //save setting in local storage
    //send them to a api to store them server sided
    console.log('settings changed');
  }

  onChangeNotice():Observable<any> {
    return this.settingsChanged.asObservable();
  }

  set targetFPS(val:number) {
    this._targetFPS = val
    this.onChange()
  }

  get targetFPS():number{
    return this._targetFPS
  }

  set showFPS(val:boolean) {
    this._showFPS = val
    this.onChange()
  }

  get showFPS():boolean {
    return this._showFPS
  }

  set enabelAntialiasing(val:boolean) {
    this._enabelAntialiasing = val
    this.onChange()
  }

  get enabelAntialiasing():boolean {
    return this._enabelAntialiasing
  }

  set viewDistance(val:number) {
    this._viewDistance = val
    this.onChange()
  }

  get viewDistance():number{
    return this._viewDistance
  }
}

在这个特定的上下文中,它是一个小游戏的设置服务-"engine" 我正在使用 THREE.js。如果我想 enable/disable 抗锯齿,它需要恢复渲染器,但如果任何其他设置已更改则不需要。

TL:DR:我必须根据发生的变化对变化做出不同的反应。

我的方法是将所有更改放在一个对象中,这样我只需要一个 setter 和一个 getter。我还将标准化更改的发出方式,以便应用程序中关心不同更改的不同部分只获得他们需要的内容。

一、更改界面:

export interface stateChange{
    propName:string,
    oldVal:any,
    newVal:any
}

接下来,设置服务:

// Stores current settings
private state = {
    fps:          30,
    showFPS:      true,
    antiAliasing: false,
    viewDistance: 7000
};

public settings = new Subject();

// Notifier is shared across all subscribers
// Notice this is not a function that returns, but the Observable itself
// (sharing needs the same Observable object, not a new one created on each func call)
public changeNotices$:Observable<stateChange> = this.settings.asObservable().share();

changeState(prop:string,val:any){
    let oldVal = this.state[prop];
    if(oldVal == val) return; // no change
    this.state[prop] = val; // save new state
    this.settings.next( // emit state change notice
        {propName:prop, oldVal:oldVal, newVal:val}
    );
}

getState(prop:string){return this.state[prop];}

如果我只关心抗锯齿,我将如何使用该服务:

antiAliasingChanges$ = this.settingsService.changeNotices$
    .filter((e:stateChange) => e.propName == 'antiAliasing')
    .subscribe((e:stateChange) => {
        if(e.newVal === true) console.log('antiAliasing is ON');
    });

这是基本代码,几乎没有检查,但您明白了。我要做的第一个改进是使用枚举,这样在最后一段代码中,我就不会硬编码 属性 名称。