今天開始 今天完成
今天目標:完成會員部分的 ngrx/store
複習一下我們的目標
會員篇 – Effects
第一步:建立 src/app/store/effects/user.effects.ts
ng generate class user.effects –spec
第二步:修改 user.effects.ts
import { Injectable } from ‘@angular/core’;
import { Action } from ‘@ngrx/store’;
import { Effect, Actions } from ‘@ngrx/effects’;
import { Observable } from ‘rxjs/Observable’;
import { of } from ‘rxjs/observable/of’;
import { map, switchMap, filter, catchError } from ‘rxjs/operators’;
import { UserService } from ‘../../userrvice/user.service’;
import { UtilsService } from ‘../..rvices/utils.service’;
import { User, Response } from ‘../../models’;
import * as actions from ‘../actions’;
@Injectable()
export class UserEffects {
constructor(
private action$: Actions,
private userService: UserService,
private utils: UtilsService
) { }
@Effect()
loginEffect$: Observable<Action> = this.action$.ofType(actions.LOGIN).pipe(
map((action: actions.LoginAction) => action.payload),
switchMap((user: User) => {
return this.userService.loginServer(user).pipe(
map((res: Response) => {
if (res.success) {
if (user.rememberMe) {
this.utils.writeToken(res.payload);
}
return new actions.LoginSuccessAction(user.username);
} else {
return new actions.LoginFailAction(res.payload);
}
}),
catchError((err) => of(new actions.LoginFailAction(err))),
)
}),
);
@Effect()
logoutEffect$: Observable<Action> = this.action$.ofType(actions.LOGOUT).pipe(
map(() => {
this.utils.removeToken();
return (new actions.LogoutSuccessAction());
})
)
@Effect()
getUserEffect$: Observable<Action> = this.action$.ofType(actions.GETUSER).pipe(
switchMap(() => {
return this.userService.getUserFromServer().pipe(
filter(user => (user !== null)),
map((user: User) => new actions.GetUserSuccessAction(user.username)),
catchError(err => of(new actions.GetUserFailAction(err))),
)
})
)
}
在 constructor() 導入動作 Actions(監聽用),使用者服務 UserService(跟後端連結)跟工具服務 UtilsService (寫入讀出 Localstorage)
Effect 函數使用 this.action$.ofType() “監聽“ 動作 (Action),處理完“副作用“的部分後,最後會輸出 Observable<Action> 另外的動作
loginEffect$ 監聽 “LOGIN” 的動作,一聽到這個動作,先從動作的payload取出資料,之前我們在定義
export class LoginAction implements Action {
readonly type = LOGIN;
constructor(public payload: User) { }
}
時,已經定義了 payload是 User 型態,接著用 switchMap 來使用 UserService,請回憶一下[ngrx/store -5] 高階 (High Order) Observable,因為 UserService.loginServer() 本身也是 Observable,我們需要用到高階運算子,如果登入成功的話,寫入從後端傳回的 token,分派一個成功的動作LoginSuccessAction 交給 Reducer,前面定義過的 Reducer,就會產生新的狀態
//…省略
switch (action.type) {
case actions.LOGOUT:
return initialState;
case actions.LOGIN_SUCCESS:
case actions.GETUSER_SUCCESS:
return { …state, currentUser: action.payload, isLogin: true };
default:
return state
}
//…
logoutEffect$ 監聽 “LOGOUT” 的動作,簡單的做清除 token
getUserEffect$ 監聽 “GETUSER” 的動作,一樣使用使用者服務,從後端取回使用者名稱,在依後端回覆的狀況,產生GetUserSuccessAction 或是 GetUserFailAction 給 Reducer
第三步:修改 src/app/store/effects/index.ts
export const effects: any[] = [UserEffects];
export * from ‘./user.effects’;
放在 effects 的陣列中,這樣 app.module.ts 就會註冊這個 effects
會員篇 – Selector
有人形容 store 就像前端的資料庫,如果狀態樹是表格 (Table) 的話,那選擇器(Selector) 就像是事先寫好的查詢(Query)一樣,複雜的 Selector 可能會跨不同的狀態樹枝,就像資料庫的 Join 般連結起使用者想要得到的資料,我們以後會看到
第一步: 建立 src/app/storelectors
lectors.ts
ng generate class selectors –spec
第二步:修改 selectors.ts
import { createSelector } from ‘reselect’;
import * as fromReducer from ‘../reducers’;
import * as user from ‘../reducers/user.reducers’;
// for selector
export const getUserState = (state: fromReducer.State) => state.user; // point to users state subtree
export const getIsLogin = createSelector(getUserState, user.getIsLogin);
export const getCurrentUser = createSelector(getUserState, user.getCurrentUser);
導入 createSelector 函數,詳情請參考官方文件,前面的參數指向狀態樹的哪一個部分,這個例子 getUserState 指向狀態樹的 state.user 樹枝(在 store/reducer/index.ts 中),後面的參數傳回從這個樹枝傳回的資料, user.getIsLogin 跟 user.getCurrentUser (這個在 user.reducer.ts 中定義)
因為我們的 selectors 相對單純,我們就先用一個檔案就好,以後複雜時,再來擴充
第三步:修改 src/app/storelectors/index.ts
export * from ‘.lectors’;
下次我們要將元件改版,從原來使用服務的地方,改成使用 ngrx/store