【问题标题】:Using async pipe in template causes multiple requests in Angular 11在模板中使用异步管道会导致 Angular 11 中出现多个请求
【发布时间】:2021-03-16 14:39:51
【问题描述】:

我正在使用 Angular v11.0.2 做一个 Web 应用程序。我需要进行 HTTP 调用以在屏幕上显示一些信息。为了节省时间并利用 async 管道的好处,我在模板中使用它,但是它会导致多个请求永不停止。

服务

export class MyDataService {

  constructor(
    private http: HttpClient,
  ) {
  }

  fetchMyEmployeeData(): Observable<any[]> {
    return this.http.post<any[]>('ENDPOINT', {});
  }

}

组件

export class MyAwesomeComponent {

  constructor(
    public myDataService: MyDataService,
  ) {
  }

}

模板

<ng-container *ngIf="(myDataService.fetchMyEmployeeData() | async) as data">
</ng-container>

这会导致多次请求并且永远不会停止。

如果我使用*ngFor,也会发生同样的情况:

<tr *ngFor="let d of (myDataService.fetchMyEmployeeData() | async)">
  <td>.</td>
</tr>

我已经尝试了以下这个:

使用shareReplay 运算符:

fetchMyEmployeeData(): Observable<any[]> {
   return this.http.post<any[]>('ENDPOINT', {}).pipe(shareReplay());
}

使用简单的 div:

<div *ngIf="(myDataService.fetchMyEmployeeData() | async) as data">
</div>

我知道如果我从组件订阅并将结果保存在局部变量中,我可以在模板中调用它,但这不是我的目标。工作示例:

export class MyAwesomeComponent implements OnInit {

  data: any[];

  constructor(
    public myDataService: MyDataService
  ) {
  }

  ngOnInit() {
    // This is not my goal because I will need to handle the subscription life manually.
    this.myDataService.fetchMyEmployeeData().subscribe(res => this.data = res);
  }

}

我也遵循了这里给出的建议:

  1. Multiple identical async pipe in Angular causing multiple http requests
  2. How can I prevent the Angular async pipe from making frequent server calls when no results come back?

我不知道究竟是什么导致了这个多次请求以及如何避免它。

我的目标是在模板中使用async 管道并只进行一次HTTP 调用。

【问题讨论】:

  • 想象一下,您在 ts 文件中编写了类似 myDataService.fetchMyEmployeeData().subscribe(); myDataService.fetchMyEmployeeData().subscribe() 的代码。尝试调试它。你会发现每个fetchMyEmployeeData() 执行都会返回一个new Observable
  • @yurzui 确实如此。所以你是说我正在尝试做的事情返回多个可观察对象,这就是为什么我看到多个请求?有道理,但现在我不明白其他人如何在没有这种行为的情况下在模板中使用 async 管道。我的意思是,在模板中使用async 的一个好处是避免处理组件中的订阅生命周期。还有一些教程展示了相同的技术,而无需尝试我的问题。你有什么建议吗?我很困惑。
  • 在你的组件中将 observable 定义为像employeeData$ = this.myDataService.fetchMyEmployeeData() 这样的属性,你会对模板中的employeeData$ | async 感到满意。
  • @andriishupta 如果我们需要在模板中使用两个异步管道,则需要它
  • 我以为是ngContainer和ngFor指令的情况,没仔细看

标签: angular angular-httpclient


【解决方案1】:

更新:如果您在模板中多次调用变量,则需要shareReplay,误解了问题。

shareReplay() 应该在组件级别,因为当您调用 fetchMyEmployeeData() 时,它是一个返回新 Observables(以及每个新的 shareReplays)的函数

在组件创建中:

employeeData$ = this.myDataService.fetchMyEmployeeData().pipe(shareReplay());

并在模板中用作

employeeData$ | async

在这种情况下,它只会对后端执行一次请求

【讨论】:

  • 即使没有shareReplay() 操作符也可以使用。但在组件中声明它不是我的目标。有没有办法将调用保留在模板中?
  • 需要使用 stackblitz 来调试它,如果不运行它很难说,但据我所知,它就是这样。另外,我已经更新了答案,以应用 @yurzui 的评论。我误读了这个问题,尽管模板中有多次调用
【解决方案2】:

详细说明为什么需要将其初始化为组件的属性。发生的事情是当 Angular 的变更检测运行时,它会看到表达式 myDataService.fetchMyEmployeeData() 并执行它。这会返回一个新的 observable 并发出请求。更改检测会继续运行,因此会被多次调用。

恕我直言,模板应该尽可能地笨拙,因为它会显示您提供给它的数据,而不是自己获取数据。

【讨论】:

    【解决方案3】:

    调用服务方法 fetchMyEmployeeData() 将始终返回 NEW observable。

    1. 当模板渲染服务方法 fetchMyEmployeeData() 被调用并返回新的 observable(重要的是它是新的)。
    2. 异步管道接收这个 observable,订阅它,完成后调用模板更新。
    3. 当模板再次更新时,一切都从循环的第 1 步开始。

    因此,如果您将创建一个组件变量并将 fetchMyEmployeeData() 的结果分配给它,它将只有 1 个唯一的 observable。并且通过异步管道订阅它不会触发无休止的http请求。

    serverData = myDataService.fetchMyEmployeeData();
    <ng-container *ngIf="serverData | async">
    </ng-container>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      • 2021-05-22
      • 2018-07-18
      • 2019-07-11
      • 2017-07-28
      相关资源
      最近更新 更多