让我们看一下您的代码的简化版本:
1 function ngOnInit() {
2 myItemsObservable$().subscribe(item => this.item = item);
3 console.log(this.item); // undefined
4 }
您实际上是在调用两个函数,它们会一个接一个地立即执行。
第 2 行创建一个订阅对象,该对象启动可观察对象内的数据流。但是......在第 2 行之后执行并没有暂停。因此,在 observable 中的异步任务完成之前执行第 3 行。这就是为什么 this.item 在第 3 行仍然是 undefined。
希望您能明白为什么您的评论不正确:
// this.item here should already be complete.
您正在将一个函数 (item => this.item = item) 传递给 subscribe() 方法,该方法在发生时处理来自可观察对象的排放。这是代码中实际发出值的位置。
所以,如果我们将console.log() 移动到订阅中,this.item 将不再是未定义的:
1 function ngOnInit() {
2 myItemsObservable$().subscribe(item => {
3 this.item = item;
4 console.log(this.item); // not undefined :-)
5 });
6 }
解决您问题的两个部分:
如何在 RxJS observable 中添加额外的字段
你已经在这样做了。您已使用 map 和 concatMap 运算符将源 observable 发出的值修改为您想要的值。
...然后等待它完成?
嗯,你不会“等待”它本身。使用 RxJS,您正在定义数据如何流动的行为。您可以访问实际数据的唯一位置是订阅内部。
但是...您可以直接在代码的其他部分直接引用 observable,而不是订阅,然后将数据从 observable 复制到另一个变量。
让我们将您的代码分成几个不同的部分,以便更容易了解我们如何在不订阅的情况下引用不同的可观察源:
id$ = this.route.paramMap.pipe( // This could come from a form control input
params => params.get('id') // or some other observable source.
);
allItems$ = myItemsObservable$(this.store, items, items.data);
getItem$(id) {
return this.allItems$.pipe(
map(items => items.find(i.id === id))
);
}
getExtraData$(id) {
return this.apiService.get(`/items/${id}/extradata`);
}
item$ = this.id$.pipe(
switchMap(id => getItem$(id)),
switchMap(item => this.getExtraData(item.id).pipe(
map(extra => ({ ...item, ...extra }))
))
);
}
看看item$ 的定义是如何以id$ 开头的?这意味着每当id$ 发出一个新值时,item$ 将自动调用getItem$(),然后getExtraData() 然后发出这个新项目。我们不需要订阅就可以做到这一点。
我们可以简单地定义一个 observable 以从另一个 observable 开始,然后 .pipe() 排放并转换它们以满足我们的需求。
我们基本上设计了一个 observable,它会在商店中的项目发生变化时发出,或者每当我们选择的 id$ 发出新值时发出。从某种意义上说,我们已经建立了item$ 来代表我们的item,并且它将始终是最新的,包括附加其“额外数据”。这是非常强大的。现在我们可以使用它了。
注意item$ 的定义不需要在ngOnInit 中;它实际上可以直接在您的组件上运行。
确实可以在我们的组件中订阅...但我们通常可以在模板中使用AsyncPipe:
<div *ngIf="item$ | async as item">
<h1>{{ item.name }}</h1>
<ul>
<li>{{ item.description }}</li>
<li>{{ item.someProperty }}</li>
</ul>
</div>
如果你发现自己经常订阅你的组件,只是为了将数据复制到一个局部变量中,供你的模板使用;我会鼓励你停下来问问自己这真的有必要。大多数情况下,您可以定义一个 observable 来准确地发出您的视图所需的数据,而无需订阅。
RxJS 提供了许多运算符和静态函数,可以轻松创建具有各种常见行为的可观察对象。