最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

ngrx/store 之路由篇

XAMPP下载 文, 员 24浏览 0评论
 ngrx/store 之路由篇
今天開始 今天完成

今天的目標:將路由狀態加入 store

基本上這個部份大都是標準程式碼 (boilerplate),也就是只要照著做就可以,而且以後也可以直接拷貝這個部分的程式,在別的專案使用,官方文件 請參考,唯一有可能要改的,大概就是 CustomSerializer,等一下會看到,依照慣例,先從動作定義起

路由篇 - Action
第一步: src/app/store/actions 下建立檔案

ng generate class router.actions –spec
第二步: 修改 router.actions.ts

import { Action } from ’@ngrx/store’;
import { NavigationExtras } from ’@angular/router’;

export const GO = ’[router] GO’;
export const BACK = ’[router] BACK’;
export const FORWARD = ’[router] FORWARD’;

export class Go implements Action {
readonly type = GO;
constructor(
public payload: {
path: any[];
query?: Object;
extras?: NavigationExtras
}
) { }
}
export class Back implements Action {
readonly type = BACK;
}
export class Forward implements Action {
readonly type = FORWARD;
}
export type Actions =
| Go
| Back
| Forward;
格式跟之前使用者動作檔一樣,三個動作,GO, BACK, FORWARD,只有 Go 有帶參數,也就是跟 router.navigate() 一樣,事實上,我們將會用 Go 來取代程式中的 router.navigate()
第三步:修改 src/app/store/actions/index.ts,加入

export * from ’./user.actions’;
export * from ’./router.actions’;
路由篇 - Reducer
第一步: src/app/store/reducers 下建立檔案

ng generate class router.reducers –spec
第二步:修改 router.reducers.ts

import { ActivatedRouteSnapshot, RouterStateSnapshot, Params } from ’@angular/router’;

import { ActionReducerMap, createFeatureSelector, Action } from ’@ngrx/store’;
import { routerReducer, RouterReducerState, RouterStateSerializer } from ’@ngrx/router-store’;

export interface RouterStateUrl {
url: string;
queryParams: Params;
params: Params;
};

export type RouterState = RouterReducerState<RouterStateUrl>;
export const reducer = routerReducer;

export class CustomeSerializer implements RouterStateSerializer<RouterStateUrl>{
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
const { url } = routerState;
const { queryParams } = routerState.root;

let state: ActivatedRouteSnapshot = routerState.root;

while (state.firstChild) {
state = state.firstChild;

}
const { params } = state;
return { url, queryParams, params };
}
}
export const getRouterState = createFeatureSelector<RouterReducerState<RouterStateUrl>>(‘routerReducer’);
State 跟 reducer 其實套件已經幫我們寫好了,我們自己只要定義我們想要的資料,將它們定義在 RouterStateUrl 中,再用 CustomeSerializer 沿著路由一層一層往下展,用 while loop,從根

 (routerState.root) 往下展開,因為路由長的樣子像是 /member/report/3,這樣找出 params,這個例子是 3
第三步:修改 src/app/reducers/index.ts

import { ActionReducerMap } from ’@ngrx/store’;

import * as user from ’./user.reducers’;
import * as router from ’./router.reducers’;
export interface State {
user: user.UsersState;
router: router.RouterState;
}
export const reducers: ActionReducerMap<State> = {
user: user.reducer,
router: router.reducer
}
export { CustomeSerializer } from ’./router.reducers’;
架構已經在,只要將 router 的 State 跟 reducer 填進即可,最後我們要告訴模組我們要的 CustomeSeriallizer
第四步:修改 src/app/app.module.ts

//… 省略
@NgModule({
//… 省略
providers: [
//... 省略
{ provide: RouterStateSerializer, useClass: fromStore.CustomeSerializer }
],
bootstrap: [AppComponent]
})
export class AppModule { }
路由篇 - Effects
第一步:在 src/app/store/effects 下建立檔案

ng generate class router.effects –spec
第二步:修改 router.effects.ts

import { Injectable } from ’@angular/core’;
import { Router } from ’@angular/router’;
import { Location } from ’@angular/common’;

import { Action } from ’@ngrx/store’;
import { Effect, Actions } from ’@ngrx/effects’;

import * as actions from ’../actions’;
import { tap, map } from ’rxjs/operators’;

@Injectable()
export class RouterEffects {
constructor(
private actions$: Actions,
private router: Router,
private location: Location
) { }

@Effect({ dispatch: false })
navigate$ = this.actions$
.ofType(actions.GO)
.pipe(
map((action: actions.Go) => action.payload),
tap(({ path, query: queryParams, extras }) => {
this.router.navigate(path, { queryParams, …extras });
}));

@Effect({ dispatch: false })
navigateBack$ = this.actions$
.ofType(actions.BACK)
.pipe(tap(() => this.location.back));

@Effect({ dispatch: false })
navigateForward$ = this.actions$
.ofType(actions.FORWARD)
.pipe(tap(() => this.location.forward));
}
Go 其實就是呼叫 router.navigate 而 Forward, Back 就是 location 的 forward, back
第三步:些修改 src/app/store/effects/index.ts

import { UserEffects } from ’./user.effects’;
import { RouterEffects } from ’./router.effects’;

export const effects: any[] = [UserEffects, RouterEffects];

export * from ’./user.effects’;
export * from ’./router.effects’;
可以看到,應用這樣的架構,加入一個新的 effects 其實就是照樣填進去就行,這樣 app.module.ts 就會註冊這個新進的 effects

檢查一下 redux devtools

QQ截图20181011160110
 可以看到狀態樹中,已經多了 router 狀態

接下來,我們將用到 router.navigate() 的地方換成 store.dispatch.(new fromStore.Go())

第一步: src/app/user/login/login.component.ts

@Component({
//… 省略
constructor(
private fb: FormBuilder,
private store: Store<fromStore.State>,
private snackbar: MatSnackBar
) { }

//… 省略
login() {
this.store.dispatch(new fromStore.LoginAction(this.form.value));
this.store.select(fromStore.getIsLogin)
.subscribe(res => {
if (res) {
this.snackbar.open(‘登入成功’, ’OK’, { duration: 3000 });
//this.router.navigate(['/member']);
this.store.dispatch(new fromStore.Go({ path: ['/member'] }));
} else {
//… 省略
將 原先導入的 Router刪除
將 router.navigate(['/member']) 改為 store.dispatch(new fromStore.Go({ path: ['/member'] }))
第二步:修改 src/apprvices/startup.service.ts,將 injector 刪除,並將 router.navigate() 換成 store.dispatch()

//… 省略
checkStatus() {
if (this.utils.isTokenExpired()) {   // if token expired or not exist
this.store.dispatch(new fromStore.LogoutAction());
this.store.dispatch(new fromStore.Go({ path: ['/'] }));
}
}
第三步:同樣方式修改 src/app/navbar/navbar.component.ts

//.. 省略
logout() {
this.store.dispatch(new fromStore.LogoutAction());
this.store.dispatch(new fromStore.Go({ path: ['/'] }));
}
這樣登入後就可以看到,狀態的變化如下

QQ截图20181011160143

转载请注明:XAMPP中文组官网 » ngrx/store 之路由篇