【问题标题】:Can't get data from combineLatest - Angular无法从 combineLatest 获取数据 - Angular
【发布时间】:2020-06-18 08:51:40
【问题描述】:

我正在尝试获取包含在帖子日志中的文件。我在这里做错了什么?当我尝试pipecombineLatest 的结果时,数据不会在链中稍后出现。整个代码用于数据解析服务。

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);
  })
)

【问题讨论】:

  • 您可以尝试在您的pipe 中添加take(1) 吗?
  • 你使用的是哪个版本的 RxJs
  • @Wilt 我正在使用 RxJs 6
  • if (response["logs"])console.log(response) 之前,确保logs 是一个数组并且它有files 这是一个数组。然后在combineLatest 上执行console.log('before combineLatest)` 以确保您到达那里。可以帮助您在 .pipe 内部进行调试的运算符是 tap 运算符。您可以通过将debugger; 放入其中或`log 语句来使用它来调试您的流。
  • 您的括号似乎不平衡。这是您的代码的逐字副本吗?

标签: javascript angular typescript rxjs resolver


【解决方案1】:

注意combineLatest 在每个 observable 发出至少一个值之前不会发出初始值。这与withLatestFrom 的行为相同,并且可能是一个陷阱,因为不会有输出也没有错误,但是您的一个(或多个)内部可观察对象可能无法按预期运行,或者订阅已延迟。

也许您的 API 调用之一是:

this.API.getFile(file.id)

返回错误或永远不会完成?

这直接from the documentation here


更新:

在提到 in your comment 使用 RxJs 6 之后,您需要知道 combineLatest 需要一个数组作为参数。

所以你应该考虑改成:

return combineLatest(
  logs["files"].map(
    file => this.API.getFile(file.id);
  ),
);

【讨论】:

  • 如果我尝试订阅它而不是管道它,我会得到 combineLatest 的值。
  • @ShabbyAbby 如果是这样的话,似乎没有任何问题;你订阅过你的管道结果吗?
  • 当我在组件中订阅this.route.data时应该会发生。
  • @ShabbyAbby 我看到另一个答案解决了你的问题......很好!
【解决方案2】:

你的问题

您不会将 combineLatest 可观察对象返回给您的 switchMap

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

以一种非常简单的方式,您目前正在这样做:

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

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

优化 observable 创建

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

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

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

// 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

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

【讨论】:

  • 感谢您对问题的非常好的解释。你是对的,combineLatest 永远不会运行。
  • 我添加了一个替代示例,我认为应该更好。首先,获取文件,然后如果有文件,则创建 observable 数组并返回 forkJoin。在我看来,它更简单、更不容易出错并且更容易理解。
  • 我已使用最后一张地图将文件连接回正确的日志。您的第二个解决方案工作得更加顺畅。
  • 您的优化解决方案运行良好。无论如何,我发现在解析器中加载文件是没有意义的,因为它可以是一个帖子下的很多文件,它会迫使用户下载所有文件而不总是需要它们。
  • @Cabuxa.Mapache 在 javascript 中,!! (bang bang) 运算符将真实值评估为布尔值。它是相反的!操作员。所以对于var a = '';!a 的计算结果为true!!a 的计算结果为false。答案不是绝对必要的。但是,如果进行严格的相等检查,这是必要的。
猜你喜欢
  • 1970-01-01
  • 2021-09-18
  • 2018-06-19
  • 2014-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
相关资源
最近更新 更多