【问题标题】:angular 5 - loop of observers inside an other observer - efficient way to do it角度 5 - 另一个观察者内部的观察者循环 - 有效的方法
【发布时间】:2019-07-09 07:33:44
【问题描述】:

我有一个“get”调用,它返回包含用户 ID 和名称的用户列表。 调用成功后,我需要为每个用户调用另一个休息来带他的照片。

目前我正在这样做-

组件-

users;
userPhotos = {};

constructor(private dashboardService: DashboardService) {
  this.dashboardService.bringUsers().subscribe((data) =>{
    this.users = data;
    this.users.forEach((user) => {
      this.dashboardService.getPicture(user.userID).subscribe((data) => {
        if (data){
          this.userPhotos[user.userID] = window.URL.createObjectURL(data);
        } else {
          this.userPhotos[user.userID] = '';
        }
      });
    });
  });
}

在我的html中-

<ng-container *ngFor="let user of users">
  <div>
      {{user.displayName}}
  </div>

  <display-picture [image]="userPhotos[user.userID]"></display-picture>
</ng-container>

其中 display-picture 是一个仅呈现图像的组件。

有没有什么高效的方法使用rxjs来做呢?

【问题讨论】:

  • 理想情况下,这应该由您正在利用的后端 API 实现。作为初始请求的一部分,API 还应响应每个用户的显示图片。
  • 但是我不希望我的初始请求会因为图片而延迟,那可能会更重。
  • 恕我直言,我认为最好一次获取必须在单个视图中显示的所有数据,而不是进行多个 API 调用来获取不同的字段。这只是需要一起显示的内容的问题。如果情况是在一个视图中只显示名称,然后一旦用户单击一个项目,然后您向他们显示另一个视图,则当前的 API 实现会更好地扩展。但是,如果您需要将名称和图像全部放在一个视图中,那么这种方法将无法很好地扩展。因此,我建议您根据 UI 的外观重新考虑该策略。
  • @SiddAjmera 我同意。这显然是关联数据,一个好的 API 应该支持附加关联数据。因此,对“用户”的单个请求应该包含属于这些用户的“照片”。不管你如何实现它 Java/PHP/C# 或 GraphGL。如果每次前端更改都必须更改 API,那么您的后端开发人员将永远更新 API。
  • @Reactgular,是的。我认为 OP 在这种情况下应该考虑 GraphQL。

标签: javascript angular typescript rxjs rxjs6


【解决方案1】:

from 迭代每个用户,mergeScan 可以在这里用来执行 observables。为清楚起见,我将其分为两个功能。也可以用mergeScan控制并发数

    const getUserPic=users=>from(users).pipe(
        mergeScan((acc, curr:any) =>
            getPicture(curr.userID).pipe(map(photo => {
                acc[curr.userID] = photo
                return acc
            }))
          ,{})
        ) 

bringUsers().pipe(
    tap(users=>this.users=users),
    switchMap(users=> getUserPic(users)),
    last()
).subscribe(console.log)

https://stackblitz.com/edit/rxjs-1bt172

【讨论】:

  • 这样我有我的初始用户对象吗?它不仅可以有显示名称。
  • 它被分配给 this.users 或者你可以根据需要沿着链传递它.. 类似switchMap(users=&gt; getUserPic(users).pipe(map(userPics=&gt;({userPics,users}))...
  • 我尝试在堆栈 Liz 中提出一个工作示例
  • 更新了答案和工作示例。嘲笑了api。希望你能明白
  • 你做的也不错,tap 是一个副作用操作符,最适合分配可观察链之外的东西
【解决方案2】:

您可以使用forkJoin() 将来自多个可观察对象的所有最终发出的值合并在一起。 RxJS 6 支持使用键/值对的对象映射来定义哪些可观察对象,但对于旧版本,您必须使用数组。

this.dashboardService.bringUsers().pipe(
   switchMap(users => forkJoin(
       users.reduce((acc, user) => (acc[user.userId] = this.dashboardService.getPicture(user.userID), acc), {}
   ))),
).subscribe((users: {[userId: number]: any}) => {
   Object.entries(users).forEach(([userId, data]:[number,any]) => {
      this.userPhotos[user.userID] = data ? window.URL.createObjectURL(data) : '';
   });
});

【讨论】:

  • 为什么我需要一系列旧版本?
  • 为什么要减少?看起来我在这个方法中失去了我的“用户”对象。我也需要原来的“用户”。
  • 从 rxjs 6.5 forkjoin 支持以 observable 作为对象值处理对象数组,reduce 可以为 forkJoin 生成一个 {key:pictureObservable} 数组以在 learnrxjs.io/operators/combination/forkjoin.html 上工作
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多