網站搜尋

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