3

我试图更好地理解如何使用 RxJS 运算符来解决我遇到的特定问题。我实际上有两个问题,但它们足够相似。

我正在从 API 端点获取一堆文档,/api/v3/folders/${folderId}/documents并设置了具有执行此功能的服务,并处理所有身份验证等。

但是,这个文档对象数组没有描述属性。为了获得描述,我需要调用/api/v3/documents/${documentId}/上一次调用中的每个文档。我的文档界面如下所示:

export interface Document {
  id: number;
  name: string;
  description: string;
}

我想我需要mergeMap等待并获取文档,一些如何将描述添加到我的Document界面并返回整个内容,但是我无法获得最终结果。

getDocuments(folderId: number) {
    return this.docService.getDocuments(folderId, this.cookie).pipe(
      map(document => {
        document; // not entirely sure what to do here, does this 'document' carry over to mergeMap?
      }),
      mergeMap(document => {
        this.docService.getDocument(documentId, this.cookie)
      }) // possibly another map here to finalize the array? 
    ).subscribe(res => console.log(res));
  }

这可能看起来有点重复,但我发现的任何帖子都没有 100% 为我清理干净。

非常感谢任何有助于理解如何在第二次调用中正确使用数据并将其包装在一起的帮助。谢谢你。

感谢@BizzyBob,这是带有编辑和解释的最终解决方案:

  getDocuments(folderId: number) {
    const headers = new HttpHeaders({
      'Content-Type': 'application/json',
      'Context': this.cookie$
    });
    return this.docService.getDocuments(folderId, this.cookie$).pipe(
      mergeMap(documents => from(documents)),
      mergeMap(document => this.http.get<IDocument>(
        this.lcmsService.getBaseUrl() + `/api/v3/documents/${document.id}`,
        { headers: headers }
      ).pipe(
        map(completeDoc => ({...document, description: completeDoc.description}))
      )),
      toArray()
    ).subscribe(docs => {
      this.documents = docs;
      console.log(this.documents);
    }
    )
  }

由于某种原因,我无法pipe()从第二个服务订阅,所以我最终不得不在http.get那里拨打电话。错误是“不能在类型订阅上使用 pipe()”,这有点令人困惑,因为我pipe()在第一次订阅时使用。我将此更改应用于更新行为主题的服务功能,并且运行良好。谢谢!

4

2 回答 2

1

有几种不同的方法可以组合来自多个 api 调用的数据。

我们可以:

  • 用于from将每个项目单独发送到流中
  • 用于mergeMap订阅辅助服务调用
  • 用于toArray在所有单独的调用完成后发出一个数组
  getDocuments() {
    return this.docService.getDocuments().pipe(
        mergeMap(basicDocs => from(basicDocs)),
        mergeMap(basicDoc => this.docService.getDocument(basicDoc.id).pipe(
          map(fullDoc => ({...basicDoc, description: fullDoc.description}))
        )),
        toArray()
    );
  } 

我们还可以利用forkJoin

  getDocuments() {
    return this.docService.getDocuments().pipe(
      switchMap(basicDocs => forkJoin(
        basicDocs.map(doc => this.docService.getDocument(doc.id))
      ).pipe(
        map(fullDocs => fullDocs.map((fullDoc, i) => ({...basicDocs[i], description: fullDoc.description})))
      )),
    );
  }

这是一个有效的StackBlitz

此外,您可以考虑将流定义为变量documents$而不是方法getDocuments()

  documents$ = this.docService.getDocuments().pipe(
    mergeMap(basicDocs => from(basicDocs))
    mergeMap(basicDoc => this.docService.getDocument(basicDoc.id).pipe(
      map(fullDoc => ({...basicDoc, description: fullDoc.description}))
    ))
    toArray()
  );
于 2020-10-10T05:00:28.427 回答
1

由于您必须调用 document/{id} 端点来获取描述,对于每个文档,您最终将作为您拥有的文档数量进行休息调用..

mergeMap返回一个 observable 并在外部 observable 发射时订阅它。(保持内部订阅与 switchMap 形成对比) https://www.learnrxjs.io/learn-rxjs/operators/transformation/mergemap

Rxjsfrom接受一个数组并返回一个 observable,按顺序发出数组中的每一项。 https://www.learnrxjs.io/learn-rxjs/operators/creation/from

因此,我们可以同时使用这两种方法来实现您的目标,如下所示:

对不起,我使用我的手机格式错误

我假设 docService.getDocuments 返回一个文档数组。

getDocuments(folderId: number) { 
    return this.docService.getDocuments(folderId, this.cookie).pipe(
      mergeMap((allDocumentsArray)=> {
             return from(allDocumentsArray).pipe(
               mergeMap((document) =>        
                   this.docservice.getDocument(document).pipe(
                        map(doc) => {...document,description})),//maps the document with document.description
     toArray(),//combines the returned values to an array
    ).subscribe(res => console.log(res));

我建议在他们的文档中阅读有关 mergemap 的信息。并在使用之前测试它,因为我没有测试过。

于 2020-10-09T19:18:58.617 回答