今天目標:完成報告有關的 Action, Reducer, Effects, Selector
第一步: src/app/store/actions 下建立檔案
ng generate class report.actions --spec
第二步:修改 report.actions.ts
import { Action } from '@ngrx/store';
import { Report } from '../../models';
// define Actions type
export const GETREPORT = '[report] GETREPORT';
export const GETREPORT_SUCCESS = '[report] GETREPORT_SUCCESS';
export const GETREPORT_FAIL = '[report] GETREPORT_FAIL';
export const RESET_REPORT = '[report] RESET_REPORT'; // reset to initial state
// define Actions classes
export class getReportAction implements Action {
readonly type = GETREPORT;
}
export class getReportSuccessAction implements Action {
readonly type = GETREPORT_SUCCESS;
constructor(public payload: Report[]) { }
}
export class getReportFailAction implements Action {
readonly type = GETREPORT_FAIL;
constructor(public payload: any) { }
}
export class resetReportAction implements Action {
readonly type = RESET_REPORT;
}
export type ReportActions
= getReportAction
| getReportSuccessAction
| getReportFailAction
| resetReportAction;
很簡單的動作, GETREPORT
預備給 Effects 用來接後端的資料,RESET
重新把 Report 歸零,預備給使用者登出的時候用
第三步:修改 src/app/store/actions/index.ts
export * from './user.actions';
export * from './router.actions';
export * from './report.actions';
第一步:在 src/app/store/reducers 下建立檔案
ng generate class report.reducers --spec
第二步:修改 report.reducers.ts
import { Action } from '@ngrx/store';
import * as actions from '../actions';
import { Report } from '../../models';
export interface ReportState {
reports: Report[];
}
export const initialState: ReportState = {
reports: []
}
export function reducer(state: ReportState = initialState, action: actions.ReportActions): ReportState {
switch (action.type) {
case actions.RESET_REPORT:
return initialState;
case actions.GETREPORT_SUCCESS:
return { ...state, reports: action.payload };
default:
return state;
}
}
// for selector
export const getReports = (state: ReportState) => state.reports;
第三步:修改 src/app/store/reducers/index.ts
import { ActionReducerMap } from '@ngrx/store';
import * as user from './user.reducers';
import * as router from './router.reducers';
import * as report from './report.reducers';
export interface State {
user: user.UsersState;
router: router.RouterState;
report: report.ReportState;
}
export const reducers: ActionReducerMap<State> = {
user: user.reducer,
router: router.reducer,
report: report.reducer
}
export { CustomeSerializer } from './router.reducers';
將 report 將入原本的 State 跟 reducers
第一步:在 src/app/store/effects 下建立檔案
ng generate class report.effects --spec
第二步:修改 report.effects.ts
import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
import { Effect, Actions } from '@ngrx/effects';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/switchMap';
import { of } from 'rxjs/observable/of';
import { map, catchError } from 'rxjs/operators';
import * as actions from '../actions';
import { ReportsService } from '../../member/services/reports.service';
import { Report, Response } from '../../models';
@Injectable()
export class ReportEffects {
constructor(
private action$: Actions,
private reportsService: ReportsService
) { }
@Effect()
getReportEffect$: Observable<Action> = this.action$.ofType(actions.GETREPORT)
.switchMap(() => {
return this.reportsService.getReportsFromServer().pipe(
map((res: Response) => {
if (res.success) {
return new actions.getReportSuccessAction(res.payload);
} else {
return new actions.getReportFailAction(res.payload);
}
}),
catchError(err => of(new actions.getReportFailAction(err))),
)
});
}
getReportEffect$
GETREPORT
的動作時,用 switchMap
高階運算子來觀察 reportService.getReportsFromServer()
,請參考 [ngrx/store -5] 高階 (High Order) Observable
getReportSuccessAction()
,否則分派一個 getReportFailAction()
import { UserEffects } from './user.effects';
import { RouterEffects } from './router.effects';
import { ReportEffects } from './report.effects';
export const effects: any[] = [UserEffects, RouterEffects, ReportEffects];
export * from './user.effects';
export * from './router.effects';
export * from './report.effects';
直接修改 src/app/store/selectors/selectors.ts
import { createSelector } from 'reselect';
import * as fromReducer from '../reducers';
import * as user from '../reducers/user.reducers';
import * as router from '../reducers/router.reducers';
import * as report from '../reducers/report.reducers';
import { Report } from '../../models';
// 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);
export const getRouterState = (state: fromReducer.State) => state.router;
export const getReportState = (state: fromReducer.State) => state.report;
export const getReports = createSelector(getReportState, report.getReports);
// join selector
export const getSelectedReport = createSelector(getRouterState, getReportState, (router, reportState): Report => {
return router.state && reportState.reports.filter(report => report.id === +router.state.params.rptId)[0];
})
getUserState, getRouterState, getReportState
)getSelectedReport
比較特別,它需要用到兩種狀態,一是路由的 params
指到的 rptId
,一是整個報告的陣列,利用陣列的 .filter()
回傳這個 rptId
的報告,當中的一些檢查(那份報告存不存在?)先省略下次我們就可以完成其他的部分(報告預載,修改報告服務,修改元件)