沒錯,又是另外一套 State management 工具,這套叫做 NGXS ,為什麼會想嘗試這一套呢? 主要是他的語法與 Angular 現有的寫法及運作方式幾乎是一樣的,學習門檻變得很低,而且,重點是要產生的檔案變得非常的少!!
安裝 NGXS
安裝 NGXS 的方式很簡單,透過 npm 安裝 @ngxs/store 即可
| 1 | npm install @ngxs/store | 
在 app.module.ts 將 NgxsModule imports 進來
| 1 | import { BrowserModule } from '@angular/platform-browser'; | 
- 當遇到 lazy loading modules 時,在該 module 下會使用 NgxsModule.forFeature([])來註冊 state;即使所有的 module 都是 lazy load 時,還是得在 root module 裡註冊NgxsModule.forRoot([])
到這個步驟時,NGXS 已經加入到 Angular 專案裡了
建立 State
NGXS 的 State  是一個單純的 class  檔案,可以透過  ng g class <<state file name>> 來產生,作者建議的檔案名稱是 [stateName].state.ts ,就此篇練習的目的,建立一個 todos.state.ts 檔案,Class 名稱為 TodosState
| 1 | export class TodoItem { | 
- @Statedecorator 用來描述- state的狀態- @State<T>:定義此 state 的資料型別
- name:該 state 在 store 裡的名稱
- defaults: 資料存放位置,(預設值)
 
- 用來描述 @State的型別,建議在最後加上 Model,例如TodoStateModel
完成建立 state 時,這時候在到 app.module.ts 內註冊到 NgxsModule.forRoot([]) 內
| 1 | ... | 
建立 Action
在 NGXS 的架構下,要設定可以被執行的 action 方法時,是不需要額外新增檔案的,直接寫在 state class 下即可
| 1 | export class ADDTODO { | 
- 
@Action內傳的 Class ,是用來定義此 Action 的名稱,在 store dispatch 時,就是根據 class 來決定所要執行的動作
- 
addTodo(StateContenxt<T>, ActionClass? )- 第一個參數是取得可操作目前 state 的 context 物件,內有的方法有
- getState():T取得目前 state 的值
- setState(val:T):any重設目前 state 的值(重新建立一個新的state)
- patchState(valu: Partial<T>)更新目前 state 的值 (不會產生一個全新 state)
- dispatch(actions)觸發 action,一個或是多個(用陣列包)
 
- 第二個參數是取得 Action 對應的 Class 實體,NGXS 是透過這樣子的模式傳遞資料
 
- 第一個參數是取得可操作目前 state 的 context 物件,內有的方法有
- 
由於 State本身是活在 Angular 的 DI 機制下,所以也可以在constructor的注入其他 service,所以當要呼叫 API 時,也可以直接寫在 action function 就可以了,不需要額外在建立檔案1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14constructor(private service: ApiService) {} 
 (ADDTODO)
 addTodo({ getState, setState }: StateContext<TodosStateModel>, { payload }: ADDTODO) {
 return this.service.someApiCall().pipe(
 tap(() => {
 const state = getState();
 setState({
 ...state,
 dataset: [...state.dataset, payload]
 });
 })
 );
 }使用 Store當 State class 寫完後,接下來就可以在各個地方透過 store 的方式做執行 action 及取得資料的行為了 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17import { Component } from '@angular/core'; 
 import { Store, Select } from '@ngxs/store';
 import { Observable } from 'rxjs/Observable';
 import { TodoItem } from './todos.state';
 ({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
 })
 export class AppComponent {
 title = 'app';
 ('todos.dataset') todos: Observable<TodoItem[]>;
 constructor(private store: Store) {}
 }- 
@Select該 decorator 允許我們透過 path 的設定取到特定的 state 資料,而取得的資料型態為 Observable- 
如果不想要這樣子寫,可以透過 store.select 的方式做設定,結果是一樣的 1 
 2
 3
 4
 5
 6
 7export class AppComponent { 
 todos: Observable<TodoItem[]>;
 constructor(private store: Store) {
 this.todos = store.select(state => state.todos.dataset);
 }
 }
 
- 
- 
執行 action 的動作,一樣是透過 store.dispatch來執行,可以傳入一個或是多個(用陣列傳) actions,而dispatch是一個 observable,並會回傳 action 後的 state 狀況,這表示我們可以用 RxJS 的方式最很多變化1 
 2
 3
 4
 5
 6
 7
 8<ul> 
 <li *ngFor="let item of todos | async">
 {{ item.content }}
 </li>
 </ul>
 <input type="text" #f />
 <button (click)="addTodo(f)">Add Todo</button>app.component.ts1 
 2
 3
 4
 5
 6addTodo(input) { 
 this.store.dispatch(new ADDTODO(input.value)).subscribe(state => {
 console.log(state);
 input.value = '';
 });
 } 
 
- 
Recap
NGXS 是以 Angular 的角度重新思考 Redux 風格的 state management,個人是覺得這樣的模式大幅降低學習及編寫的門檻,而 NGXS 內還有更多的功能都有在電子書上說到,有興趣的人真的可以動手玩看看,寫起來跟寫 Angular service 真的沒什麼差異。