我已经使用Angular / RxJS了几周了,并且具有基于多个REST请求构建的各种模型,到目前为止,我已经使用switchMap()实现了该模型。这是一个简单的示例(stackblitz:https://stackblitz.com/edit/angular-o1djbb):
import { Component, OnInit, } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay, switchMap } from 'rxjs/operators';
interface Order {
id: string;
itemName: string;
details?: string;
}
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
order: Order;
ngOnInit() {
this.getOrderFromApi(123)
.subscribe(item => this.order = item);
}
getOrderFromApi(id): Observable<Order> {
const item$ = this.getItemName(id);
const fullItem$ = item$.pipe(
switchMap(n => {
console.log(`Got name: '${n}''`);
return this.getDetails(n);
},
(nameReq, detailsReq) => ({ id: '123', itemName: nameReq, details: detailsReq })
));
return fullItem$;
}
getItemName(id): Observable<string> {
return this.fakeXhr('foo');
}
getDetails(itemName): Observable<string> {
console.log(`Got details '${itemName}''`)
return this.fakeXhr('Some details about foo');
}
fakeXhr(payload: any) {
return of(payload)
.pipe(delay(2000));
}
}
还有一个简单的模板:
<p>
item: {{order && order.itemName}}
</p>
<p>
details: {{order && order.details}}
</p>
这有效,但是在完成两者请求之前,订单信息不会呈现。我想让 itemName 尽快呈现,然后在细节可用时呈现细节。因此,利用了Observables可以发出的多个值。例如:
// first value emitted:
{ itemName: 'foo', details: null }
// second value emitted:
{ itemName: 'foo', details: 'Some details about foo' }
我意识到我可能可以通过BehaviourSubject或Redux来实现(就像我过去使用React一样),但是由于所有这些对我来说都是新事物,所以我觉得我没有一个简单的解决方案。
答案 0 :(得分:2)
使用expand
直接从第一个请求发出数据,然后执行第二个请求。
expand
将递归调用内部请求,从上一个请求中获取order
作为输入,因此我们仅在order
没有details
时执行第二个请求并以EMPTY
Observable结束递归。
import { Observable, EMPTY } from 'rxjs';
import { map, expand } from 'rxjs/operators';
getOrderFromApi(id): Observable<Order> {
return this.getItemName(id).pipe(
map(itemName => ({ id, itemName, details: null } as Order)),
expand(order => order.details
? EMPTY
: this.getDetails(order.itemName).pipe(
map(details => ({ id, itemName: order.itemName, details } as Order))
)
)
);
}
https://stackblitz.com/edit/angular-umqyem
返回的Observable将发出:
{ "id": 123, "itemName": "foo", "details": null }
{ "id": 123, "itemName": "foo", "details": "Some details about foo" }
答案 1 :(得分:0)
也许最好的选择是用case playeragain = 11 (binary 1011)
case player = 1 (Binary 0001)
case GBullet = 2. (Binary 0010)
case BBullet = 3. (Binary 0011)
case enemyships = 4. (Binary. 0100)
case coin = 5. (Binary 0101)
case boss = 6. (Binary 0110)
代替mergeMap()
?
https://blog.angular-university.io/rxjs-higher-order-mapping/
如我们所见,合并的源Observable的值显示出来 立即输出。结果“可观察”将不会完成 直到所有合并的Observables完成。
我一定会看一下这些文章:
答案 2 :(得分:0)
您可以将接收到的值存储在代码的各个部分中,即在它们可用之后,而不是将整个项目保留在可观察流的末尾(即,在.subscribe
块中)
switchMap(n => {
console.log(`Got name: '${n}''`);
this.order.name = n;
return this.getDetails(n);
},
...,然后在sibscription块中,您只需存储高级信息:
this.getOrderFromApi(123)
.subscribe(item => this.order.details = item.details);
很显然,这要求您相应地初始化“ order”属性,例如与order: Order = {};
或order: Order = new Order();
。
基本上,这个想法是重组您的代码。您的可观察流将发出部分值(您订单的属性),而不是完整的对象。
答案 3 :(得分:0)
我想出的一个解决方案是只创建一个Observable并调用next()。我很想听听对此的任何看法。
getOrder(id) {
const ob$ = new Observable(subscriber => {
const item$ = this.getItem(id)
.pipe(
tap(o => subscriber.next(({ id: id, itemName: o.itemName, details: null }))),
switchMap(o => this.getDetails(o.itemName),
(o, d) => ({ id: id, itemName: o.itemName, details: d.details })
),
tap(a => subscriber.next(a))
);
item$.subscribe(a => {
subscriber.complete();
});
});
return ob$;
}
Stackblitz:https://stackblitz.com/edit/angular-mybvli