The "transition to reactive" really should be all or nothing, IMO. This is a really good recent article on this topic in general.
As for Angular2 applications specifically, this means that you want to model things like threads everywhere, from end to end - from HTTP responses that deliver data to the templates used to display it.
So, in your case, not:
@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; }) } }
you want to do something like:
@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) //ie user ? false : true } }
The key point here is that you model the state of your application as a stream entirely from the service (which supposedly relays the observed API response) to the template component, where you use AsyncPipe , so that angular can handle all the dirty UI subscription and update actions, to reflect changes as needed.
In response to @MarkRajcok's comment:
Speaking of subscribe () ... don't you need one in your last line ngOnInit ()?
No, and this is a really important point. The beauty of AsyncPipe is that you don’t have to manually sign up for anything, just let angular do it for you. This wraps up the minefields of potential problems with detecting changes that might result from manually processing these things.
But how do you deal with errors? For example, suppose you get an error message when you try to get a user from the backend. If you want to use NgIf to display an error or display a user, you do not need to manually subscribe () and break the chain?
Not necessary. Observable.catch () is quite useful for these purposes:
@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); } }
That being said, my message here is not that you never need to manually subscribe to things (of course, there are situations where this is the case), but rather that we should avoid this when possible. And the more comfortable I get with rx, the less often I understand what these situations are.