【问题标题】:Loop array and return data for each id in Observable循环数组并返回 Observable 中每个 id 的数据
【发布时间】:2019-08-26 06:06:54
【问题描述】:

使用 RxJS v6 从子集合中检索循环中每个项目的数据具有挑战性。无法迭代从 HTTP 调用中检索到的 throw 数组。 Merge map 仅对需要对所有数组项执行此操作的单个项执行此操作。

我已经坚持了 3 天。我什么都试过了。问题是新的管道语法虽然为您提供了组织代码的简洁方法,但没有简单的方法来循环抛出数据集合。无法使用 map javascript 函数,因为它在 observable 之外并且返回的项目是未定义的。在互联网上,我只能找到一个效果很好的单个 mergeMap 的基本示例。但是我需要使用 数组中的每个项目都带有 http 调用并将响应附加到同一对象。

我从我无法控制的第 3 方的两个端点开始。我已经模拟了一些示例 API 来说明。

端点 1 - 返回所有项目 ID: http://localhost:3000/联系方式

{
  "name": "test",
  "contact": [
    {
      "_id": "5c9dda9aca9c171d6ba4b87e"
    },
    {
      "_id": "5c9ddb82ca9c171d6ba4b87f"
    },
    {
      "_id": "5c9ddb8aca9c171d6ba4b880"
    }
  ]
}

端点 2 - 返回单项信息: http://localhost:3000/contact/5c9dda9aca9c171d6ba4b87e

[
   {
     "_id": "5c9dda9aca9c171d6ba4b87e",
     "firstName": "Luke",
     "lastName": "Test",
     "email": "vgadgf@adsgasdg.com",
     "created_date": "2019-03-29T08:43:06.344Z"
   }
]

期望的结果 - 通过使用 RxJs v6+ 返回带有嵌套数据的 Observable:

{
  "name": "test",
  "contact": [
    {
      "_id": "5c9dda9aca9c171d6ba4b87e".
      "relationship":  {
                         "_id": "5c9dda9aca9c171d6ba4b87e",
                         "firstName": "Luke",
                         "lastName": "Test",
                         "email": "vgadgf@adsgasdg.com",
                         "created_date": "2019-03-29T08:43:06.344Z"
                       }
    },
    {
      "_id": "5c9ddb82ca9c171d6ba4b87f",
      "relationship": {
                         "_id": "5c9ddb82ca9c171d6ba4b87f",
                         "firstName": "Name2",
                         "lastName": "Test2",
                         "email": "vgadgf@adsgasdg2.com",
                         "created_date": "2019-03-29T08:43:06.344Z"
                       }
    },
    {
      "_id": "5c9ddb8aca9c171d6ba4b880",
      "relationship": {
                         "_id": "5c9ddb8aca9c171d6ba4b880",
                         "firstName": "Name3",
                         "lastName": "Test3",
                         "email": "vgadgf@adsgasdg3.com",
                         "created_date": "2019-03-29T08:43:06.344Z"
                       }
    }
  ]
}

我只是在尝试使用 Javascript 循环时遇到错误,并且使用不同的运算符并没有给我太多,因为问题在于 RxJs 中的循环。这是我制作的代码。请提供任何有用的建议或文档。

testService.service.ts

import { Injectable } from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {concatMap, map, mergeMap} from 'rxjs/operators';

@Injectable()
export class TestServiceService {

  constructor(
    private http: HttpClient
  ) { }

  public getCombinedData(): Observable<any> {
    return this.getMultipleRelationData()
      .pipe(
        map((result: any) => {
          result = result.contact;

          return result;
        }),
        mergeMap((fullResults: any) => {
          return fullResults[0].relationship = this.getSingleData(fullResults[0]._id);
        })
      );
  }

  public getSingleData(id): Observable<any> {
    return this.http.get('http://localhost:3000/contact/' + id);
  }

  public getMultipleRelationData(): Observable<any> {
    return this.http.get('http://localhost:3000/contact');
  }

}

我当前的最终数据看起来只是一个项目。如果我只想打一个电话,那就太好了……但是它与上述期望的输出相去甚远。

  [
  {
    "_id": "5c9dda9aca9c171d6ba4b87e",
    "firstName": "Luke",
    "lastName": "Test",
    "email": "vgadgf@adsgasdg.com",
    "__v": 0,
    "created_date": "2019-03-29T08:43:06.344Z"
  }
]

【问题讨论】:

    标签: angular rxjs observable angular7 rxjs6


    【解决方案1】:

    这很可能使用嵌套的mergeMapmap。坚持使用 Observable 的一个关键方法是使用 from 函数将联系人数组扩展为可观察对象。然后使用toArray 运算符收集数据。提供文档示例:

    public getCombinedData(): Observable<any> {
      return this.getMultipleRelationData()
          .pipe(
              mergeMap((result: any) =>
    
                  // `from` emits each contact separately 
                  from(result.contact).pipe(
                      // load each contact
                      mergeMap(
                          contact => this.getSignleData(contact._id),
                          // in result selector, connect fetched detail 
                          (original, detail) => ({...original, relationship: detail})
                      ),
                      // collect all contacts into an array
                      toArray(),
                      // add the newly fetched data to original result
                      map(contact => ({ ...result, contact})),
                  )
              ),
          );
    }
    

    编辑:结果选择器已弃用

    在内部mergeMap,后面可以使用map

    public getCombinedData(): Observable<any> {
      return this.getMultipleRelationData().pipe(
          mergeMap((result: any) =>
              // `from` emits each contact separately
              from(result.contact).pipe(
                  // load each contact
                  mergeMap(
                      contact =>
                          this.getSignleData(contact._id).pipe(
                              map(detail => ({ ...contact, relationship: detail })),
                          ),
                      // collect all contacts into an array
                      toArray(),
                      // add the newly fetched data to original result
                      map(contact => ({ ...result, contact })),
                  ),
              ),
          ),
      );
    }
    

    【讨论】:

    • 非常感谢。奇迹般有效。如果你在伦敦,我会给你买啤酒。
    • 嘿抱歉,愚蠢的问题来了(我是 RXJS 中的菜鸟),这是否保证按请求发出的顺序添加调用结果?
    • 不,不是。如果您想保持排序,请使用concatMap 而不是mergeMap(第二次在代码中使用)或在最后一次map 中以不同方式处理结果(您有原始的和新获取的)。
    【解决方案2】:

    虽然 kvetis 的答案对你有用,但这是我实现的,只是发布它是因为我发现解决它很有趣。

      public getCombinedData(): Observable<any> {
        return this.getMultipleRelationData()
          .pipe(
            mergeMap((result: any) => {
              let allIds = result.contact.map(id => this.getSingleData(id._id));
              return forkJoin(...allIds).pipe(
                map((idDataArray) => {
                  result.contact.forEach((eachContact, index) => {
                    eachContact.relationship = idDataArray[index];
                  })
                  return result;
                })
              )
            })
          );
      }
    

    这是您问题的示例解决方案,我制作了虚拟 Observables。 https://stackblitz.com/edit/angular-tonn5q?file=src%2Fapp%2Fapi-calls.service.ts

    【讨论】:

    • 我将尝试此实现并报告性能变化。看起来更整洁的代码风格。
    • 我想说使用 forkJoin 要简单得多。 GJ。
    猜你喜欢
    • 2021-07-17
    • 2013-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 2014-03-05
    • 1970-01-01
    相关资源
    最近更新 更多