2017-08-22 2 views
1

J'ai un peu d'expérience avec JavaScript et angulaire mais je ne peux pas tout à fait obtenir ma tête autour de certains concepts. J'essaye de Construire un service pour stocker certains paramètres qui sont en mesure de notifier les composants qui l'implémentent des changements ont eu lieu.Comment construire une interface pour setters dans un service angular4 +?

La mise en place de setters qui appellent une fonction onChange pour chaque propriété a semblé être le bon vieux temps et j'ai réussi à le faire fonctionner. Mais je ne me sens pas comme la voie angulaire, comme il gonfle la classe et pour chaque propriété que je veux ajouter, je devrais mettre en œuvre de nouvelles méthodes get & get. Donc, je voudrais avoir une interface qui appelle la fonction onChange chaque fois qu'une propriété a été définie à quelque chose de nouveau. Mais je ne peux pas trouver la syntaxe pour cela.

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 
    } 
} 

Dans ce contexte particulier, il est un service pour les paramètres un peu jeu- « moteur » je travaille avec Three.js. Il doit réinstater le moteur de rendu si je veux activer/désactiver l'anti-aliasing mais pas si d'autres paramètres ont changé.

TL: DR: Je dois réagir différemment en fonction de ce qui a changé.

Répondre

0

Mon approche serait de mettre tous les changements dans un objet, de sorte que je n'ai besoin que d'un setter et d'un getter. Je voudrais également normaliser comment un changement est émis, de sorte que les différentes parties de l'application qui se soucient des différents changements obtiennent seulement ce dont ils ont besoin.

Tout d'abord, l'interface de changement:

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

Ensuite, les paramètres de service:

// 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];} 

Voici comment je consommerais le service, si je ne me souciais de l'anti-aliasing:

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

Ceci est un code de base avec peu de contrôles, mais vous avez l'idée. Une des premières améliorations que je ferais serait d'utiliser enums, de sorte que dans le dernier bit de code, je ne coderais pas le nom de la propriété.

+0

merci pour cette réponse rapide! La seule préoccupation qui vient à l'esprit sur une glace de poing est que ce setter ne vérifie pas le type – MaDsaiboT

+0

@MaDsaiboT vous avez raison. Il y a plusieurs préoccupations. Comme je l'ai écrit, "* Ceci est un code de base avec quelques contrôles *" Ce n'est pas un code de production, mais quelque chose pour vous mettre dans la direction que je voudrais aller. Vous pourriez facilement implémenter la vérification de type si vous le vouliez en ayant un 'Map ' et en vérifiant par rapport à cela avant de définir les propriétés. – BeetleJuice

+0

@MaDsaiboT Comme alternative, vous pouvez demander à 'changeState()' d'accepter un objet 'StateInterface' (une interface avec les propriétés de' state'). Rendez simplement toutes les propriétés facultatives dans l'interface, et ajustez le code 'changeState()' pour lire quelle propriété a été fournie et pour copier les valeurs dans la propriété 'state' locale. Cela assurera la vérification du type. Il a l'avantage supplémentaire de vous permettre de modifier de nombreux paramètres à la fois, tout en n'émettant qu'une seule notification de modification. C'est juste du code plus complexe mais c'est ce que j'ai fait dans ma propre application – BeetleJuice