"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.
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()'?) –
@MarkRajcok grandi domande - vedi modifica – drewmoore
Grazie per le note aggiuntive, molto apprezzate. Tuttavia, sarebbe utile se mostrassi come usare 'hasError $' con NgIf (ma giocherò con esso.) –