# Angular 的狀態管理 — 使用 Observable Data Service > 利用 RxJS 的 BehaviorSubject,將原本儲存在各 Component 中的狀態,集中儲存在 Service 中,以方便各階層的元件透過 DI 存取,而不需要透過元件間的互動來傳遞狀態數值。 ## 顯示元件單純化 狀態集中管理 以一個簡單的元件互動來做範例 ![](https://i.imgur.com/2AUWg7u.png) InputComponent 包含一個 input 輸入框,而 ShowComponent 僅是一個灰底的區塊,職責是顯示 input 輸入的數值。而兩個元件並不在元件內部儲存變數值,而是透過 StateService 存取共用的狀態值。這兩個元件程式碼分別如下: **InputComponent** ```javascript= // InputComponent 的 HTML <input #input placeholder="請輸入文字" (input)="change(input.value)" /> // InputComponent Class export class InputComponent implements OnInit { constructor(private stateService: StateService) { } ngOnInit() { } // 當 input 改變時,通知 stateService change(value) { this.stateService.change(value); } } ``` **ShowComponent** ```javascript= // ShowComponent 的 HTML // 用 async pipe subscribe observable <div>{{value$ | async}}</div> // ShowComponent Class export class ShowComponent implements OnInit { // 利用 BehaviorSubject 儲存資料,所以需要使用 Observable type value$: Observable<string> = this.stateService.value$; constructor(private stateService: StateService) { } ngOnInit() { } } ``` ## 使用 BehaviorSubject 的狀態儲存器 StateService 利用 BehaviorSubject 來儲存狀態。 先來談談 Subject。Subject 可以接受 Observer 的訂閱,也可以用來訂閱其他的 Observable,所以他本身既是 Observable 又是 Observer。在他收到所訂閱的 Observable 發出的通知時,會同時通知所有訂閱他的 Observer。 所以 Subject 等同是讓多個 Observer 可以訂閱同一個 Observable 實例,在 Subject 所訂閱的這個 Observable 發出值時,每個訂閱這個 Subject 的 Observer 都收到通知。 BehaviorSubject 繼承自 Subject,因此當然也具有同樣的功能,然而兩者最主要的差異,在於: 1. BehaviorSubject 可以接受給定初值,而 Subject 不可以。 2. Subject 只會在被訂閱之後,而所訂閱的 Observable 有發出新值時,才會做通知。而 BehaviorSubject 會在每一 Observer 訂閱時,對其發出目前已收到的最新值。 也因此 BehaviorSubject 這種類似『狀態暫存』的模式,很適合用來做狀態管理之用。 **StateService** ```htmlmixed= @Injectable({ providedIn: 'root' }) export class StateService { private store$: BehaviorSubject<string> = new BehaviorSubject<string>(''); readonly value$: Observable<string> = this.store$.asObservable(); constructor() { } change(value: string): void { this.store$.next(value); } } ``` - 我們使用 `store$: BehaviorSubject<string>` 做為狀態儲存,並且給定初值為空字串。 - 為了避免讓其他元件直接操作 `store$`,可能會有權限太大的問題(例如 BehaviorSubject 可以調用 next(),而 Observable 不行),利用 `asObservable()` 將 BehaviosSubject 轉換成單純的Observable 再賦值給 `value$`,而 `value$` 是實際供外部取用的變數。 - 提供 `change()` 讓外部元件呼叫,用以更新 `store$` 中的數值。 如此,在每次 InputComponent 中的 input 改變時,便會向 StateService 發出通知,並將新的值塞進 store$ 中,而 store$ 也會透過 value$ 立即通知每個訂閱的 observer。 ## 不同階層元件都可以同時收到新狀態值 如果我們新增一個元件 CounterComponent,要取用並顯示 InputComponent 中輸入字串的長度時,我們同樣也只需要在元件內設定: ```htmlmixed= value$: Observable<string> = this.stateService.value$; ``` 並在模板內使用 async pipe 訂閱,例: ```htmlmixed= <div>{{ (value$ | async).length }}</div> ``` 在每次 store$ 內的狀態發生改變時,CounterComponent 也會同時收到通知 ![](https://i.imgur.com/1qw2t18.png) 透過 Observable Data Service 集中管理狀態,無論元件是否間隔了很多階層,都可以更簡便地存取狀態值! [本範例 source code](https://github.com/kristxeng/Angular-Observable-Data-Service-Demo)