我正在尝试通过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
。相反,它坚持使用旧值,然后突然将其替换为更新后的值。
答案 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>