2017-08-22 28 views
1

我有一些JavaScript和角度的經驗,但我不能讓我的腦海中圍繞一些概念。 我想構建一個服務來存儲一些設置,能夠通知組件發生變化。如何在angular4 +服務中爲setter構建一個接口?

爲每個屬性設置調用onChange函數的setter似乎是老式的方式,我得到它的工作。但我不覺得像角度的方式,因爲它膨脹的類和每個屬性我想添加我將需要實現新的獲取&設置方法。 所以我想有一個接口,每次將屬性設置爲新的時候都會調用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 
    } 
} 

在這種特殊背景下它是一個小遊戲 - 「引擎」我與three.js所工作的一個設置服務。它需要重新渲染渲染器,如果我想啓用/禁用消除鋸齒,但不是如果任何其他設置已更改。

TL:DR:我必須根據變化做出不同的變化。

回答

0

我的方法是將所有更改放在一個對象中,這樣我只需要一個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'); 
    }); 

這是幾個檢查的基本代碼,但你明白了。我要做的第一個改進之一是使用枚舉,所以在最後一段代碼中,我不會硬編碼屬性名稱。

+0

感謝您的快速回答!唯一值得關注的問題是,這位製片人沒有檢查 – MaDsaiboT

+0

@MaDsaiboT你是對的。有幾個問題。正如我寫的,「*這是基本的代碼,幾乎沒有檢查*」這不是生產代碼,而是讓你走向我想去的方向。如果您希望在設置屬性之前擁有「Map 」並檢查它,可以輕鬆實現類型檢查。 – BeetleJuice

+0

@MaDsaiboT作爲替代方案,您可以要求'changeState()'接受'StateInterface'對象(具有'state'屬性的接口)。只需在接口中使所有屬性可選,並調整'changeState()'代碼以讀取提供的屬性並將值複製到本地'state'屬性。這將確保類型檢查。它還具有額外的好處,即允許您一次更改多項設置,同時僅發佈一項更改通知。這只是更復雜的代碼,但這是我在我自己的應用程序 – BeetleJuice