RxJs subscribers, passing null values?

荒凉一梦 提交于 2019-11-30 09:02:46

"Going reactive" really needs to be an all-or-nothing thing, IMO. This is a really good recent article on this topic in general.

With regard to Angular2 apps specifically, what this means is that you want to model things as streams everywhere, from end to end - from the HTTP responses that deliver data to the templates used to display it.

So in your case, rather than:

@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'd 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) //i.e. user ? false : true
  }
}

The key thing to note here is you're modeling your application state as a stream all the way from the service (which presumably is relaying an observable API response) to the component to the template, where you leverage AsyncPipe to let angular deal with all the dirtywork of subscribing and updating the UI to reflect changes as needed.

In response to @MarkRajcok's comment:

Speaking of subscribe()... don't you need one on your last line of ngOnInit()?

No, and that's actually an important point. The beauty of AsyncPipe is that you don't have to manually subscribe to anything, just let Angular do it for you. This sidesteps a minefield of potential change-detection problems that can arise from handling these things manually.

But how do you deal with errors? E.g., suppose you get an error when you try to get a user from the backend. If you want to use NgIf to either display an error or display the user, don't you have to manually subscribe() and "break the chain"?

Not necessarily. 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 isn't that it's never necessary to manually subscribe to things (of course there are situations when it is) but rather, that we should avoid doing it wherever possible. And the more comfortable I get with rx, the rarer I realize those situations are.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!