typescript - RxJs-prenumeranter, skicka nullvärden?
Jag har tillbringat dagen med att fördjupa mig i RxJS med Angular2, eftersom praktiken att modellera användargränssnitt som strömmar är ny för mig.
Jag experimenterar med en användartjänst som tillhandahåller ett flöde av User-objekt. Det första User-objektet tillhandahålls när användaren är autentiserad. Ytterligare User-objekt kan tillhandahållas när användaren uppdateras, t.ex. när de uppdaterar sin profil. Om användaren inte är inloggad när applikationen laddas, eller om de loggar ut, sänds null.
Som sådan skulle en observatörsimplementation i en komponent se ut så här:
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;
})
}
}
Det userService.user$ observerbara objektet är av typen BehaviorSubject. Är det så här du skulle implementera detta? Idén att skicka null till en ström som förväntar sig ett User-objekt känns inte helt rätt för mig. Men samtidigt ger det ett bekvämt sätt att svara på frågan: är användaren tillgänglig eller inte?
Svar #1
Att "bli reaktiv" behöver verkligen vara en allt-eller-inget-ting, enligt min mening. Detta är en riktigt bra ny artikel om detta ämne generellt.
När det gäller Angular2-appar specifikt innebär detta att du vill modellera saker som strömmar överallt, från början till slut - från HTTP-svaren som levererar data till de mallar som används för att visa den.
Så i ditt fall, snarare än:
@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;
})
}
}
du skulle vilja göra något som:
@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
}
}
Det viktigaste att notera här är att du modellerar ditt applikationstillstånd som en ström hela vägen från tjänsten (som förmodligen vidarebefordrar ett observerbart API-svar) till komponenten till mallen, där du använder AsyncPipe för att låta Angular hantera allt det smutsiga arbetet med att prenumerera och uppdatera UI:t för att återspegla ändringar vid behov.
Som svar på @MarkRajcoks kommentar:
På tal om subscribe()... behöver du inte en på din sista rad i ngOnInit()?
Nej, och det är faktiskt en viktig punkt. Det fina med AsyncPipe är att du inte behöver prenumerera på något manuellt, låt bara Angular göra det åt dig. Detta undviker ett minfält av potentiella problem med ändringsdetektion som kan uppstå när man hanterar dessa saker manuellt.
Men hur hanterar du fel? Till exempel, anta att du får ett fel när du försöker hämta en användare från backend. Om du vill använda NgIf för att antingen visa ett fel eller visa användaren, måste du då inte manuellt prenumerera() och "bryta kedjan"?
Inte nödvändigtvis. Observable.catch() är ganska användbart för dessa ändamål:
@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);
}
}
Med det sagt är mitt budskap här inte att det aldrig är nödvändigt att manuellt prenumerera på saker (självklart finns det situationer när det är det) utan snarare att vi bör undvika att göra det när det är möjligt. Och ju mer bekväm jag blir med rx, desto mer sällsynta inser jag att dessa situationer är.
