异步管道中的请求仅在前面的条件为真时触发

Request in async pipe is triggered only when preceding condition is true

我的模板中有一个 ngIf,类似于:

<div *ngIf="selectedStudents?.length > 0 && persistedPreferences | async as studentPreferences; else someTemplate">

代码目前部分是反应式的,部分是命令式的。我正在从命令式迁移到 declarative/reactive 范式。

selectedStudents 只是一个常规的对象数组,通过手动订阅从后端收到的响应来强制初始化(现在不能大量重构)。 但是,persistedPreferences 是一个可观察对象,它将从 http 请求(使用异步管道)获得响应。这部分是reactive/declarative.

因为 persistedPreferences 将被异步管道订阅,它的 http 请求只有在第一个条件为真时才会触发。这意味着我必须先等待收到 selectedStudents 响应,然后才会触发 persistedPreferences 请求。

因此,作为用户,我最终等待的时间稍长(在较慢的网络上)。 关于如何处理的任何想法?有什么提示吗?

注意:我们需要等待两个请求完成后用户才能看到任何内容。而且。来自两者的数据进入两个不同的组件。

我可以想到几个替代方案:

  1. 暂时将异步事物移到其他条件之前(不确定如何!)
  2. 也许稍微修改一下 - 使 selectedStudents 成为一个 observable 并将其与 persistedPreferences 组合成一个单独的 observable 并在模板中使用它?

像这样:

this.combinedObs = combineLatest([selectedStudentsObs, persistedPreferences]).pipe(
   map(([res1, res2] => { students: res1, prefs: res2 }))
)

然后像这样使用它:

*ngIf="combinedObs  | async as combinedResult"
<comp1 [students]="combinedResult.students"></comp1>
<comp2 [prefs]="combinedResult.prefs"></comp2>

但是第二种方式似乎不是一个很好的解决方案。考虑性能和错误处理。 被选中的学生才是真正重要的数据,一个http错误需要模糊的告知用户。首选项只是可选的东西(即使他们的 http 请求有一些错误,我们仍然可以显示选定的学生)。

第二种方式是优雅的方式,可以并行触发两个 HTTP 请求。但是,由于您提到两者都是 HTTP 请求,因此 forkJoincombineLatest 更合适。

您可以处理错误,或在组件控制器中执行任何 side-effects。

控制器 (*.ts)

import { forkJoin, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';

combineObs$: Observable<any>;
selectedStudents$: Observable<any>;
prefs$: Observable<any>;

this.selectedStudents$ = this.http.get('url').pipe(
  tap((students: any) => {            // performing side-effects
    this.selectedStudents = students;
    // do something else
  }),
  catchError((error: any) => {
    // show error to the user
    return throwError(error);         // `catchError` must return an observable
  })
);

this.prefs$ = this.http.get('url');

this.combinedObs$ = forkJoin({
  students: this.selectedStudents$,
  prefs: this.prefs$
});

模板(*.html)

<ng-container *ngIf="(combinedObs$ | async) as combinedResult">
  <comp1 [students]="combinedResult.students"></comp1>
  <comp2 [prefs]="combinedResult.prefs"></comp2>
</ng-container>