RxJs 订阅者,传递空值?

RxJs subscribers, passing null values?

我花了一整天时间研究 RxJS 和 Angular2,因为将用户界面建模为流的做法对我来说是新的。

我正在试验提供用户对象流的用户服务。第一个 User 对象将在用户通过身份验证时提供。更新用户时可能会提供其他 User 对象,例如他们更新了他们的个人资料。如果用户在应用程序加载时未登录,或者他们注销,则发出 null

因此,组件中的观察者实现如下所示:

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;
    })
  }
}

userService.user$ observable 是 BehaviorSubject 类型。这是你如何实现的吗?将 null 发送到需要 User 对象的流的想法并不适合我。但与此同时,它确实提供了一种方便的方式来回答这个问题:用户是否可用?

"Going reactive" 真的需要孤注一掷,IMO。 This 是关于这个主题的一篇非常好的近期文章。

关于 Angular2 个应用程序,这意味着您希望将事物建模为流无处不在,从头到尾 - 从 HTTP 响应将数据传送到用于显示它的模板。

所以在你的情况下,而不是:

@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;
    })
  }
}

你会想做这样的事情:

@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
  }
}

这里要注意的关键是,您将应用程序状态建模为从服务(大概是中继可观察的 API 响应)到组件再到模板的流,其中您利用 AsyncPipe 让 angular 处理订阅和更新 UI 的所有脏活,以根据需要反映更改。

回应@MarkRajcok 的评论:

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

不,这实际上很重要。 AsyncPipe 的美妙之处在于您 不必 手动订阅任何内容,让 Angular 为您完成。这避开了手动处理这些事情可能引起的潜在变化检测问题的雷区。

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"?

不一定。 Observable.catch() 对于这些目的非常有用:

@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);

  }
}

话虽这么说,我在这里的意思并不是说 从来没有 需要手动订阅(当然也有需要的情况),而是我们应该尽可能避免这样做。而且我对 rx 越熟悉,我就越少意识到这些情况。