iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 29
0
Modern Web

ngrx/store 4 學習筆記系列 第 29

[ngrx/store-29] ngrx/store 之報告篇

ngrx/store 之報告篇

今天開始 今天完成

今天目標:完成報告有關的 Action, Reducer, Effects, Selector

報告篇 - Action

第一步: 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';

報告篇 - Reducer

第一步:在 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

報告篇 - Effects

第一步:在 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))),
            )
        });
}
  1. 這裡只有一個 effect,也就是 getReportEffect$ 
  2. 當監聽到 GETREPORT 的動作時,用 switchMap 高階運算子來觀察 reportService.getReportsFromServer(),請參考 [ngrx/store -5] 高階 (High Order) Observable
  3. 成功的話,分派一個 getReportSuccessAction(),否則分派一個 getReportFailAction()
    第三步:修改 src/app/store/effects/index.ts
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';

報告篇 - Selector

直接修改 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];
})
  1. 分別從 State 抓到狀態樹的根 (getUserState, getRouterState, getReportState)
  2. export 原來 reducer 下的 selector
  3. getSelectedReport 比較特別,它需要用到兩種狀態,一是路由的 params指到的 rptId,一是整個報告的陣列,利用陣列的 .filter() 回傳這個 rptId 的報告,當中的一些檢查(那份報告存不存在?)先省略

下次我們就可以完成其他的部分(報告預載,修改報告服務,修改元件)


上一篇
[ngrx/store-28] ngrx/store 之路由篇
下一篇
[ngrx/store-30] ngrx/store 完成篇
系列文
ngrx/store 4 學習筆記30

尚未有邦友留言

立即登入留言