Observable 基礎與初始化

Observable 是什麼?

想像你在追蹤快遞包裹:

  • 你可以隨時查看包裹的狀態

  • 狀態會不斷更新(運送中、已送達等)

  • 你可以隨時取消追蹤

在 Angular 中,Observable 就像這個追蹤系統:

// 宣告一個 Observable
location$: Observable<Location[]>;  // 慣例:Observable 變數名稱後面加 $

Observable 的初始化方式

1. 直接從服務獲取(最常用)

export class SearchComponent {
  // 直接賦值服務方法返回的 Observable
  location$ = this.masterService.getLocations();
  
  constructor(private masterService: MasterService) {}
}

2. 明確宣告型別並初始化

export class SearchComponent {
  // 方法 1:使用 undefined
  location$: Observable<Location[]> | undefined;

  // 方法 2:使用 new Observable()
  location$: Observable<Location[]> = new Observable<Location[]>();
}

在服務中建立 Observable

@Injectable({
  providedIn: 'root'
})
export class LocationService {
  constructor(private http: HttpClient) {}

  // 返回位置列表的 Observable
  getLocations(): Observable<Location[]> {
    return this.http.get<Location[]>('/api/locations');
  }
}

為什麼要用陣列型別?

// Location[] 表示「位置陣列」
location$: Observable<Location[]>

// 因為 API 通常返回一個列表:
[
  { id: 1, name: "台北", code: "TPE" },
  { id: 2, name: "台中", code: "TCH" },
  { id: 3, name: "高雄", code: "KHH" }
]

在模板中使用 Observable

<!-- 使用 async pipe 訂閱 Observable -->
@for (location of location$ | async; track location.id) {
  <div>{{ location.name }}</div>
}

常見使用場景

1. HTTP 請求

// 服務中
getLocations(): Observable<Location[]> {
  return this.http.get<Location[]>('/api/locations');
}

// 組件中
locations$ = this.locationService.getLocations();

2. 表單值變更監聽

// 監聽表單變更
searchForm = new FormControl('');
searchResults$ = this.searchForm.valueChanges.pipe(
  debounceTime(300),
  switchMap(term => this.search(term))
);

3. 數據更新

private locationSubject = new BehaviorSubject<Location[]>([]);
locations$ = this.locationSubject.asObservable();

updateLocations(newLocations: Location[]) {
  this.locationSubject.next(newLocations);
}

注意事項

  1. 記得使用 async pipe 或取消訂閱

// 不好的做法:可能造成記憶體洩漏
this.location$.subscribe(data => {
  this.locations = data;
});

// 好的做法:使用 async pipe
<div *ngFor="let location of location$ | async">
  1. 型別定義要清楚

// 不好的做法
location$: Observable<any>;

// 好的做法
location$: Observable<Location[]>;
  1. 初始值的考慮

// 如果不確定是否有值,可以使用 undefined
location$: Observable<Location[]> | undefined;

// 如果確定會有值,直接賦值
location$ = this.locationService.getLocations();

小結

  • Observable 是處理異步數據的強大工具

  • 命名慣例使用 $ 結尾

  • 善用 async pipe 自動處理訂閱

  • 記得定義明確的型別

  • 選擇合適的初始化方式

Last updated