Javascript RxJs/Angular从多个HTTP请求加载并从每个请求更新模型

Javascript RxJs/Angular从多个HTTP请求加载并从每个请求更新模型,javascript,angular,rxjs,Javascript,Angular,Rxjs,我已经使用Angular/RxJS几个星期了,并且有各种模型,它们是从多个REST请求构建的,到目前为止,我已经使用switchMap()实现了这些请求。下面是一个简单的人为示例(stackblitz:): 我意识到我可能可以通过行为主题或使用Redux来实现这一点(就像我过去在React中所做的那样),但我觉得有一个简单的解决方案我不能完全掌握,因为这一切对我来说都是新的 也许你最好的选择是用mergeMap()代替switchMap() 如我们所见,合并的源观测值显示出来 立即输入输出。可

我已经使用Angular/RxJS几个星期了,并且有各种模型,它们是从多个REST请求构建的,到目前为止,我已经使用switchMap()实现了这些请求。下面是一个简单的人为示例(stackblitz:):


我意识到我可能可以通过行为主题或使用Redux来实现这一点(就像我过去在React中所做的那样),但我觉得有一个简单的解决方案我不能完全掌握,因为这一切对我来说都是新的

也许你最好的选择是用
mergeMap()
代替
switchMap()

如我们所见,合并的源观测值显示出来 立即输入输出。可观察到的结果将无法完成 直到所有合并的观测值都完成

我一定会看看这些文章:

  • (上文引述)


您可以将收到的值存储在代码的各个部分中,即在它们可用后,而不是将您的完整项保存在可观察流的末尾(即在
.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={}
订单:订单=新订单()


基本上,这个想法是重新构造代码s.t。您的可观察流会发出部分值(订单的属性),而不是完整的对象。

我提出的一个解决方案是只创建一个可观察流并调用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:

用于直接从第一个请求发出数据,然后执行第二个请求

expand
将递归调用内部请求,从上一个请求中获取
order
作为输入,因此我们仅在
order
没有
详细信息时执行第二个请求,否则将以
空的
可见结束递归

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))
      )
    )
  );
}
import{Observable,EMPTY}来自'rxjs';
从'rxjs/operators'导入{map,expand};
getOrderFromApi(id):可观察{
返回此.getItemName(id).pipe(
映射(itemName=>({id,itemName,details:null}按顺序)),
展开(order=>order.details
空的
:this.getDetails(order.itemName).pipe(
映射(details=>({id,itemName:order.itemName,details}as order))
)
)
);
}

返回的可观察对象将发出:

  • {“id”:123,“itemName”:“foo”,“details”:null}
  • {“id”:123,“itemName”:“foo”,“details”:“关于foo的一些细节”}

  • 一个直接的替代品肯定不会起作用:叉出那堆闪电,告诉我们怎么做!在这个精心设计的示例中,这可能会起作用,但实际上getOrderFromApi()将在服务中,然后实现上述功能的唯一方法是将对对象的引用传递给它getOrderFromApi(This.order),这并不是我真正想要的。我希望从返回的可观察对象发出多个值。我将此标记为答案,因为我认为这是最优雅的解决方案(至少目前是这样)。我们可以进一步使用expand中的索引,比如:谢谢,
    expand
    也非常适合执行未知数量的请求,比如在处理分页时。
    this.getOrderFromApi(123)
          .subscribe(item => this.order.details = item.details);
    
    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$;
      }
    
    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))
          )
        )
      );
    }