2016-05-20 19 views
8

Ho passato il giorno a fare immersioni in RxJS con Angular2, poiché la pratica della modellazione delle interfacce utente come flussi è nuova per me.Sottoscrittori RxJs, passando valori nulli?

Sto sperimentando un servizio utente che fornisce un flusso di oggetti Utente. Il primo oggetto User verrà fornito quando l'utente è autenticato. Ulteriori User Gli oggetti possono essere forniti quando l'utente viene aggiornato, ad es. aggiornano il loro profilo. Se l'utente non viene registrato quando l'applicazione viene caricata, o se si disconnette, viene emesso null.

Come tale, un'implementazione osservatore in un componente sarebbe così:

export class AppComponent { 
    private user: User; 
    private showLoginForm: boolean; 
    constructor(public userService: UserService) { } 
    ngOnInit() { 
    this.userService.user$.subscribe(user => { 
     this.user = user; 
     this.showLoginForm = this.user ? false : true; 
    }) 
    } 
} 

Il userService.user$ osservabile è un tipo BehaviorSubject. È così che vorresti implementarlo? L'idea di inviare null a uno streaming che si aspetta un oggetto User non è adatta a me. Ma allo stesso tempo, fornisce un modo conveniente per rispondere alla domanda: l'Utente è disponibile o no?

risposta

13

"Going reactive" ha davvero bisogno di essere una cosa tutto o niente, IMO. This è un ottimo articolo recente su questo argomento in generale.

Per quanto riguarda le app Angular2 in particolare, ciò significa che si desidera modellare le cose come flussi ovunque, da capo a capo - dalle risposte HTTP che forniscono dati ai modelli utilizzati per visualizzarlo.

Quindi nel tuo caso, piuttosto che:

@Component({ 
    template: ` name: {{ user?.name }` //elvis operator always needed with this approach 
}) 
export class AppComponent { 
    private user: User; // breaks the chain 

    ngOnInit() { 
    this.userService.user$.subscribe(user => { 
     this.user = user; 
    }) 
    } 
} 

che ci si vuole fare qualcosa di simile:

@Component({ 
    template: ` name: {{ (user$ | async).name }` //let angular deal with that shit 
}) 
export class AppComponent { 
    private user$: Observable<User>; // stream :) 
    private showLoginForm$: Observable<boolean>; 

    ngOnInit() { 
    this.user$ = this.userService.user$; //could actually be done in constructor 
    this.showLoginForm$ = this.user$.map(user => !user) //i.e. user ? false : true 
    } 
} 

La cosa fondamentale da notare qui è che sei modellazione tuo stato applicazione come un flusso completamente dal servizio (che presumibilmente sta inoltrando una risposta API osservabile) al componente del modello, in cui si fa leva su AsyncPipe per lasciare che si occupino angolarmente di tutto il lavoro sporco di sottoscrizione e aggiornamento dell'interfaccia utente per riflettere le modifiche necessarie.

In risposta al commento di @ MarkRajcok:

Parlando di sottoscrizione() ... non avete bisogno di uno sulla vostra ultima linea di ngOnInit()?

No, e questo è in realtà un punto importante. La bellezza di AsyncPipe è che tu non hai iscriverti manualmente a qualcosa, lascia che Angular lo faccia per te. Questo elude un campo minato di potenziali problemi di rilevamento del cambiamento che possono sorgere dalla gestione manuale di queste cose.

Ma come si gestiscono gli errori? Ad esempio, supponiamo di ricevere un errore quando tenti di ottenere un utente dal back-end. Se si desidera utilizzare NgIf per visualizzare un errore o visualizzare l'utente, non è necessario sottoscrivere manualmente() e "interrompere la catena"?

Non necessariamente. è molto utile per questi scopi:

@Component({ 
    template: ` <div>name: {{ (user$ | async).name }</div> 
       <div *ngIf="hasError$ | async">ERROR :("></div>` 
}) 
export class AppComponent { 
    private user$: Observable<User>; 
    private showLoginForm$: Observable<boolean>; 

    private hasError$: Observable<boolean>; 
    private error$: Observable<string>; 

    ngOnInit() { 
    this.user$ = this.userService.user$; 
    this.showLoginForm$ = this.user$.map(user => !user) 

    this.hasError$ = this.user$.catch(error => true).startWith(false); 
    this.error$ = this.user$.catch(error => error.message); 

    } 
} 

Detto questo, il mio messaggio qui non è che si tratta di mai necessario iscriversi manualmente alle cose (naturalmente ci sono situazioni in cui è), ma piuttosto, che dovremmo evitare di farlo ovunque possibile. E più mi sento a mio agio con rx, più raro mi rendo conto di quelle situazioni.

+1

Bello, ma come si gestiscono gli errori? Ad esempio, supponiamo di ricevere un errore quando tenti di ottenere un utente dal back-end. Se vuoi usare NgIf per visualizzare un errore o visualizzare l'utente, non devi manualmente "subscribe()" e "break the chain"? (Parlando di 'subscribe()' ... non ne hai bisogno sull'ultima riga di 'ngOnInit()'?) –

+0

@MarkRajcok grandi domande - vedi modifica – drewmoore

+0

Grazie per le note aggiuntive, molto apprezzate. Tuttavia, sarebbe utile se mostrassi come usare 'hasError $' con NgIf (ma giocherò con esso.) –