# Angular 的狀態管理 — 使用 Observable Data Service
> 利用 RxJS 的 BehaviorSubject,將原本儲存在各 Component 中的狀態,集中儲存在 Service 中,以方便各階層的元件透過 DI 存取,而不需要透過元件間的互動來傳遞狀態數值。
## 顯示元件單純化 狀態集中管理
以一個簡單的元件互動來做範例

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 也會同時收到通知

透過 Observable Data Service 集中管理狀態,無論元件是否間隔了很多階層,都可以更簡便地存取狀態值!
[本範例 source code](https://github.com/kristxeng/Angular-Observable-Data-Service-Demo)