typescript - 在 Angular 中使用 RxJS 的宣告式/反應式資料存取方式時,如何「傳遞」資料?
在 Angular 中,資料存取的經典模式建議像這樣的程式碼:
經典資料存取模式
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(this.productsUrl)
.pipe(
tap(data => console.log(JSON.stringify(data))),
catchError(this.handleError)
);
上面我們有一個返回 Observable 的方法。當資料從 http 端點返回時,該 Observable 會以由 Product[] 泛型參數定義的形狀發出資料,其中 Product 是一個介面。
對於更具反應性的應用程式,通常建議使用宣告式/反應式的方法。它將上述方法改為像這樣的屬性宣告:
反應式資料存取模式
products$ = this.http.get<Product[]>(this.url)
.pipe(
tap(data => console.log(JSON.stringify(data))),
catchError(this.handleError)
);
注意這裡在服務中為 Observable 聲明了一個屬性,而不是方法。$ 後綴出現在 products$ 上,是一種慣例,用來區分屬性是 Observable 還是其他類型的數據結構,例如一般對象或陣列。
當存在使用者介面互動(例如基於選擇進行篩選)、處理複雜資料(例如來自多個端點結合資料)以及在組件之間共享資料(例如當每個組件需要在資料變化時做出反應)時,這種模式經常被使用。
反應式數據訪問模式呈現了一個常見問題。在使用這種技術時,我們如何「傳遞」URL所需的數據?
例如,假設這個 HTTP 請求需要一個分類,以便只抓取該分類的產品:
this.http.get<Product[]>(`${this.url}?cat=${catId}`)
既然沒有方法,這個類別如何能被「傳入」呢?
答案#1
與其思考如何「傳遞」數據,不如思考如何隨時間「發出」該值。要發出數據,我們使用 Subject 或 BehaviorSubject 定義我們自己的 Observable。
private categorySubject = new Subject<number>();
categorySelectedAction$ = this.categorySubject.asObservable();
products$ = this.categorySelectedAction$.pipe(
switchMap(catId=>this.http.get<Product[]>(`${this.url}?cat=${catId}`))
.pipe(
tap(data => console.log(data)),
catchError(this.handleError)
));
selectedCategoryChanged(categoryId: number): void {
this.categorySubject.next(categoryId);
}
在上述程式碼中,我們使用 Subject 定義了我們的 Observable。我們將 Subject 宣告為私有,以便無法從服務的外部訪問它。然後,我們使用 asObservable() 來公開該 Subject 的唯讀部分。
每當使用者選擇不同的類別時,元件會呼叫 selectedCategoryChanged,該函式會將所選的 categoryId 發送到我們的 Subject 定義的流中。
對於 products$,當一個值被發出時,它會通過一系列運算子。switchMap 是一個高階映射運算子,它會自動訂閱內部 Observable(即 this.http.get...)並將結果扁平化。指定類別返回的產品隨後會被發射到 products$ 流中。
要查看更完整的解決方案,請參見這個 GitHub: https://github.com/DeborahK/Angular-RxJS/tree/master/APM-Final
