哈囉,各位邦友們!
昨天我們把資料抽到 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 狀態。
明天會學習如何加上路由,讓載入流程在切換頁面的時候能順暢地銜接。