Angular RxJS-设置value将管道值更改为null直到可观察到的解析

时间:2018-08-27 03:49:53

标签: javascript angular typescript rxjs

我正在尝试通过select传递valueChanges来触发适当的API请求,并在尚未收到响应的情况下显示微调框。最重要的是,我尝试使用publish()refCount(),以便可以在模板中的多个位置使用async管道的可观察值。

my-component.ts

ngOnInit() {
  this.results$ = this.selectControl.valueChanges.pipe(
    startWith('hard'),
    switchMap((evalStrategy: 'hard' | 'soft') => this.apiService.getResults(evalStrategy)),
    publish(),
    refCount()
  );
}

my-component.html

<mat-progress-spinner *ngIf="!(results$ | async)"></mat-progress-spinner>

<div *ngIf="results$ | async">
   <p>Name {{(results$ | async).name}}</p>
   <small>{{(results$ | async).description}}
</div>

理想情况下,我希望这段代码能够正常工作,并且只会向服务器发出 1个请求

但是我最终得到Cannot read property 'name' of null:(

如果我对 my-component.html 进行以下更改

<mat-progress-spinner *ngIf="!(results$ | async)"></mat-progress-spinner>

<div *ngIf="(results$ | async) as results">
   <p>Name {{(results).name}}</p>
   <small>{{(results).description}}
</div>

然后它可以正常工作,并且向服务器发出唯一的火警请求1。但是,当您更改选择选项时,在新值可用之前,results$ | async将不再求值为null。相反,它坚持使用旧值,然后突然将其替换为更新后的值。

1 个答案:

答案 0 :(得分:0)

理想情况下,您希望分离出变量-以便上下文更清晰-loading和最终results的状态是两件事。假设results等于null等于status正在加载可能很危险,因为它们本质上是不同的。例如,如果有错误怎么办?这将意味着results为空,而status不再加载。如果遵循您的实现,则抛出错误时,微调器将永远加载。而且它也可能遇到您所描述的情况-您丢失了loading的状态,现在cos不再null,并且您的微调框在第一个初始请求之后将永远不会加载。

每次在表单中进行更改时,将results重置为null也是一种冗余(毫无意义)。从技术上讲,您的结果不是null。它有结果。只是结果不是最新的。

相反,使用主题发出值:

private isLoading = new BehaviourSubject<boolean>(false);

在您的组件中,只需使用.tap()来更改Subject的状态:

this.results$ = this.selectControl.valueChanges.pipe(
    startWith('hard'),
    tap(() => this.isLoading.next(true)), //sets loading to true, we are firing our api request!
    switchMap((evalStrategy: 'hard' | 'soft') => this.apiService.getResults(evalStrategy)),
    tap(() => this.isLoading.next(false)), //api finished fetching, stop the spinner!
    catchError(() => this.isLoading.next(false)), //this is up to you on how you want to do error handling.
    publish(),
    refCount(),
);

并获取您的html绑定到它:

<mat-progress-spinner *ngIf="isLoading"></mat-progress-spinner>