首页 > 解决方案 > 无法从 combineLatest 获取数据 - Angular

问题描述

我正在尝试获取包含在帖子日志中的文件。我在这里做错了什么?当我试图pipe得到combineLatest. 整个代码用于数据解析服务。

return this.API.getPost(route.params.id).pipe(
  switchMap((response: any) => {
    if (response["logs"]) {
      response["logs"].map(logs => {
        if (logs["files"]) {
          return combineLatest(
            ...logs["files"].map(file=>
              this.API.getFile(file.id)
            )
          ).pipe(
            // Not getting any files from this.API.getFile(file.id)
            map(files =>
              files.map(file => ({
                url: this.sanitizer.bypassSecurityTrustResourceUrl(
                  window.URL.createObjectURL(file)
                )
              }))
            )
          ),
            map(files => {
              logs["files"] = files;
              return response;
            });
        }
      });
    }
    return of(response);
  })
)

标签: javascriptangulartypescriptrxjsresolver

解决方案


你的问题

您不会将combineLatestobservable 返回给您的switchMap.

在您的if块中switchMap,您只是在创建一个未使用的可观察对象映射。

以一种非常简化的方式,您当前正在执行以下操作:

switchMap(response =>
  if (response["logs"]) {
    response["logs"].map(logs => of(logs));
  }

  return of(response);
}

我已将您的最新组合简化为一个of来演示问题。当if条件通过时,该块将创建一个新数组,然后立即将其忽略。这意味着无论您的 if 条件如何,switchMap都将始终调用of(response). 您的combineLatest阵列将永远不会运行。

一个解法

if你需要从你的块中返回某种可观察的。如果您考虑您正在创建的数据类型,它是一个可观察的数组。因此,为此您需要forkJoin运行一个可观察对象数组并返回一个switchMap可以切换到的可观察对象。

return this.API.getPost(this.route.params.id).pipe(
  switchMap((response: any) => {
    if (response["logs"]) {
      return forkJoin(response["logs"].map(logs => {
        if (logs["files"]) {
          return combineLatest(
            ...logs["files"].map(file=>
              this.API.getFile(file.id)
            )
          ).pipe(
            map(files =>
              files.map(file => ({
                url: this.sanitizer.bypassSecurityTrustResourceUrl(
                  window.URL.createObjectURL(file)
                )
              }))
            )
          ),
            // Not sure what this is???
            map(files => {
              logs["files"] = files;
              return response;
            });
        }
      }));
    }
    return of(response);
  })
)

此外,我不确定map我评论的目的是什么 - 它目前没有任何用途,甚至可能导致编译问题。

演示:https ://stackblitz.com/edit/angular-5jgk9z

这个演示是对您的问题的简单抽象。“不工作”版本会创建一个不返回的可观察对象映射。“工作”版本切换到 aforkJoin并返回它。在这两种情况下,保护if块的条件都为真。

优化可观察的创建

我认为内部可观察对象的创建可以简化并变得更安全。

当您可以直接使用时,将数组包装combineLatest在 a 中似乎有点多余。forkJoinforkJoin

为了更清楚,我会将数组映射与可观察的创建分开。这也将帮助您避免最终将空数组放入combineLatestor的错误forkJoin

// inside the "if" block

// flatten files
const files = response["logs"]
  .filter(log => !!log.files)
  .map(log => log.files)
  .flat();

if (files.length > 0) {
  const observables = files.map(file => this.API.getFile(file.id));
  return forkJoin(observables).pipe(
    map(files => {
      files.map(file => ({
        url: this.sanitizer.bypassSecurityTrustResourceUrl(
          window.URL.createObjectURL(file)
        )
      }))
    })
  );
}

这使用了.flat()数组函数,该函数采用多维数组,例如:

[
 [1,2,3],
 [4,5,6]
]

并将其展平为:

[ 1,2,3,4,5,6 ]

如果您需要 IE 支持并且没有 polyfill,您可以使用此处的替代方法:https ://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat 。

我还建议创建一些接口并使用强类型。如果你仅仅依赖于好的变量命名,你很快就会迷失方向(当然,我们从来没有给变量起过坏的名字……)


推荐阅读