[英]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.