[英]Reactive Loading state management with RxJs
当你有一些输入字段并且你必须在值更改时获取一些东西时的经典任务。 假设我们使用 Angular Reactive Forms。示例:
orders$ = inputControl.valueChanges.pipe(
switchMap((value) => {
return someService.fetch(value);
})
);
现在我们还应该以某种方式管理加载 state。我通常使用tap
:
orders$ = inputControl.valueChanges.pipe(
tap(() => { loading = true }), // or loading$.next(true) if loading is a subject
switchMap((value) => {
return someService.fetch(value);
}),
tap(() => { loading = false }), // or loading$.next(false) if loading is a subject
);
然而,我们似乎可以通过某种方式避免在tap
中赋值,而是使用 RxJs 代替。 但是我找不到处理它的方法。
对我来说,理想解决方案的用法是
orders$ = <some abstraction here that depends on inputControl.valueChanges and fetching>
loading$ = <some abstraction here that depends on fetching>
您可以使用 map 和 shareReplay 运算符来实现此目的。 map 运算符可用于从服务调用返回值,shareReplay 运算符可用于共享可观察对象并保持加载 state。
orders$ = inputControl.valueChanges.pipe( switchMap((value) => { return someService.fetch(value).pipe( map(data => { loading$.next(false); return data; }), startWith({loading: true}) ); }), shareReplay(1) ); loading$ = orders$.pipe(map(data => data.loading));
这样您就可以使用 orders$ observable 来订阅订单,并使用 loading$ observable 来订阅加载 state。注意:如果您从服务调用返回 object,上面的示例将不起作用,您需要将 object 包装在另一个 object 具有加载属性,如 {data: {}, loading: true/false}
看看我的图书馆 ez-state。
https://github.com/adriandavidbrand/ngx-ez/tree/master/projects/ez-state
orderCache = new EzCache<Order[]>();
order$ = this.orderCache.value$;
loading$ = this.orderCache.loading$;
inputControl.valueChanges.subscribe(value => {
orderCache.load(someService.fetch(value));
});
EzCache 是一个美化的行为主题,它具有加载、保存、更新和删除方法,并管理 state 个可观察对象,如加载 $、加载 $、保存 $、保存 $ 等。
我个人会在服务中拥有缓存,并像我写的那篇文章那样公开服务中的可观察对象。
如果您将加载 state 视为更大的“订单搜索请求”的一部分,那么就更容易理解如何使用 RxJS 创建一个发出所需整体 state 的可观察对象。
不用两个单独的 observable, orders$
和loading$
,你可以有一个单独的 observable 发出两条数据(因为它们总是同时改变)。
我们基本上想要创建一个最初发出的可观察对象:
{
isLoading: true,
results: undefined
}
然后,在收到结果后,发出:
{
isLoading: false,
results: ** orders from api call **
}
我们可以通过使用switchMap
、 map
和startWith
来实现:
orderSearchRequest$ = this.searchTerm$.pipe(
switchMap(term => this.searchService.search(term).pipe(
map(results => ({ isLoading: false, results })),
startWith({ isLoading: true, results: undefined })
)),
);
<form [formGroup]="form">
<input formControlName="searchTerm" placeholder="Order Search">
</form>
<div *ngIf="orderSearchRequest$ | async as request">
<div *ngIf="request.isLoading"> loading... </div>
<ul>
<li *ngFor="let order of request.results">
{{ order.description }}
</li>
</ul>
</div>
这是一个有效的StackBlitz演示。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.