哈囉,各位邦友們!
昨天我們把資料抽到 HeroService,並用 inject() 在元件中取用。
今天來試著把同步的 getAll 改為 Observable,並在元件中處理 loading 與 error,學習非同步資料流的使用。
getAll$(),使用 RxJS 模擬非同步。loading/error 狀態。一、HeroService:將目前同步資料包成 Observable 模擬延遲
// src/app/hero.service.ts
// 新增:引入 rxjs 工具
import { delay, of, throwError } from 'rxjs';
// ...existing code...
@Injectable({ providedIn: 'root' })
export class HeroService {
  // ...existing code...
  // 新增:以 Observable 回傳資料,加入 delay 模擬網路延遲
  getAll$() {
    return of(this.getAll()).pipe(delay(2000));
    // return throwError(() => new Error(`此為人工製造錯誤`));
  }
  // ...existing code...
}
重點:
of() 將目前同步資料包成 Observable。delay(2000) 模擬 2 秒網路延遲。二、App 元件:訂閱資料流與狀態管理
// src/app/app.ts
import { Component, DestroyRef, inject, signal } from '@angular/core';
// ...existing code...
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@Component({
  // ...existing code...
})
export class App {
  // 注入服務與 DestroyRef
  private readonly heroService = inject(HeroService);
  private readonly destroyRef = inject(DestroyRef);
  // 狀態:標題、英雄清單、目前選中的英雄、載入、錯誤
  protected readonly title = signal('hero-journey');
  protected readonly heroes = signal<Hero[]>([]);
  protected readonly selectedHero = signal<Hero | null>(null);
  protected readonly heroesLoading = signal(true);
  protected readonly heroesError = signal<string | null>(null);
  constructor() {
    // 從 Observable 取得資料
    this.heroService
      .getAll$()
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe({
        next: (list) => {
          this.heroes.set(list);
          this.heroesLoading.set(false);
        },
        error: (err) => {
          this.heroesError.set(String(err ?? 'Unknown error'));
          this.heroesLoading.set(false);
        }
      });
  }
  // ...existing code...
}
重點:
takeUntilDestroyed(this.destroyRef) 自動解除訂閱。heroesLoading = true,完成或失敗後關閉 loading。三、範本 UI:Loading/Error/List
<!-- src/app/app.html -->
<!-- ...existing code... -->
@if (heroesLoading()) {
  <p class="muted">Loading heroes...</p>
} @else if (heroesError(); as e) {
  <p class="error">Load failed: {{ e }}</p>
} @else {
  <section>
    <!-- ...existing list code... -->
  </section>
}
<!-- ...existing code... -->
四、樣式:錯誤狀態
/* src/app/app.scss */
/* ...existing code... */
.error {
  color: #c33;
}
/* ...existing code... */
驗收清單:
 
getAll$() 改為 throwError), 
 
常見錯誤與排查:
takeUntilDestroyed:從 @angular/core/rxjs-interop 匯入。heroes() 與 heroesLoading()。takeUntilDestroyed(this.destroyRef)。hero.service.ts 匯出 Hero 型別。今日小結:
今天把 HeroService 從原本的同步讀取改成用 Observable 的 getAll$(),試著用訂閱的方式來處理非同步資料,並且搭配 signals 來管理 loading 和 error 狀態。
明天會學習如何加上路由,讓載入流程在切換頁面的時候能順暢地銜接。