集成Redux
集成Redux¶
我们还是以Get操作为例,新建第一个Redux,后面章节继续扩展增加、更新、删除
1. 修改actions
在第一个程序中的[3. State]中,我们生成了初始的+state,默认创建了三个Action:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // content-hrmanager.actions.ts /** * 加载用户Action */ export const loadContentHrmanager = createAction( '[ContentHrmanager] Load ContentHrmanager' ); /** * 加载全部用户成功Action */ export const loadContentHrmanagerSuccess = createAction( '[ContentHrmanager] Load ContentHrmanager Success', props<{ contentHrmanager: ContentHrmanagerEntity[] }>() ); /** * 加载失败Action */ export const loadContentHrmanagerFailure = createAction( '[ContentHrmanager] Load ContentHrmanager Failure', props<{ error: any }>() ); |
2. 修改effects
从actions流里面获取到加载action(如:loadContentHrmanager)之后,利用service和api交互。成功的场合,返回最新的数据。失败的场合,返回error信息。
我们可以利用下面两种方式,来捕获相关action。
-
直接从action流里面获取
- 构造方法中注入 hrManagerService
1 2 3 4 5 6 | // content-hrmanager.effects.ts // 1. 将HrManagerService引入进来 import { HrManagerService } from '@redux-app/backend'; // 2. 构造方法中注入 constructor(private actions$: Actions, private hrManagerService: HrManagerService) {} |
1 | - 改造loadContentHrmanager$里面run函数 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | // 改造前 this.actions$.pipe( ofType(ContentHrmanagerActions.loadContentHrmanager), fetch({ run: (action) => { // Your custom service 'load' logic goes here. For now just return a success action... return ContentHrmanagerActions.loadContentHrmanagerSuccess({ contentHrmanager: [], }); }, onError: (action, error) => { console.error('Error', error); return ContentHrmanagerActions.loadContentHrmanagerFailure({ error }); }, }) // 改造后 loadContentHrmanager$ = createEffect(() => this.actions$.pipe( ofType(ContentHrmanagerActions.loadContentHrmanager), fetch({ run: (action) => { return this.hrManagerService.getAll().pipe( // 正常,发出成功action map(data => (ContentHrmanagerActions.loadContentHrmanagerSuccess({contentHrmanager: data}))), // 异常,发出失败action catchError(err => of(ContentHrmanagerActions.loadContentHrmanagerFailure({ error: (err as HttpErrorResponse).message })) ) ); }, onError: (action, error) => { console.error('Error', error); return ContentHrmanagerActions.loadContentHrmanagerFailure({ error }); }, }) ) ); |
-
通过DataPersistence获取action
- 构造方法中注入 dataPersistence
1 2 3 4 5 6 7 8 | // 头部引入 import { DataPersistence, fetch } from '@nrwl/angular'; import { State } from './content-hrmanager.reducer'; // 构造方法中注入 constructor(private actions$: Actions, private hrManagerService: HrManagerService, private dataPersistence: DataPersistence<State>) {} |
1 | - load方法改造如下 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * 加载数据 */ loadContentHrmanager$ = createEffect(() => this.dataPersistence.fetch( ContentHrmanagerActions.loadContentHrmanager, { run: (action) => { return this.hrManagerService.getAll().pipe( // 正常,发出成功action map(data => (ContentHrmanagerActions.loadContentHrmanagerSuccess({contentHrmanager: data}))), // 异常,发出失败action catchError(err => of(ContentHrmanagerActions.loadContentHrmanagerFailure({ error: (err as HttpErrorResponse).message })) ) ); }, onError: (action, error) => { console.error('Error', error); return ContentHrmanagerActions.loadContentHrmanagerFailure({ error }); } } ) ); |
3. 改造form
我们在form.component.ts中注入facade,来发射加载action来请求数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // form.component.ts // 1. 定义用户集合 /** * 用户集合 */ user$: Observable<ContentHrmanagerEntity[]>; // 2. 构造方法中注入facade constructor(private hrManagerService: HrManagerService, private hrManagerFacade: ContentHrmanagerFacade) {} // 3. search方法改造如下: // 发出加载全部用户数据action this.hrManagerFacade.dispatch(loadContentHrmanager()); // 获取所有用户集合 this.user$ = this.hrManagerFacade.allContentHrmanager$; // form.component.html // 在"删除"按钮下面添加如下代码 <div *ngFor="let user of (user$ | async)"> <label>{{user.id}} -- </label> <label>{{user.name}} -- </label> <label>{{user.age}}</label> </div> |
上面三步做完后,将下面的步骤执行下:
- 安装store-devtools, 便于从chrome的redux的插件中,观察状态的变化。
1 | npm i @ngrx/store-devtools --save-dev |
- app.module.ts中引入StoreDevtoolsModule
1 2 3 4 5 | // 1. 头部手动导入 import { StoreDevtoolsModule } from '@ngrx/store-devtools'; // 2. imports中添加 StoreDevtoolsModule.instrument(), |
- package.json加入json-server
1 2 3 | // json-server启动配置在环境中 // package.json -> scripts中加入下面这句话 "mock": "json-server --watch mock/_fixture/hr-manager.json --port 3000" |
json-server配置完成后,我们可以在NPM SCRIPTS中点击server就可以运行mock数据了

4. 测试
当我们启动mock和程序后,点击查询按钮,我们会发现页面上可以看到user数据。

我们也可以在form.component.ts的构造方法中,注入action流,来监听错误的action信号/事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // app.component.ts // 1. 头部引入actions包 import * as ContentHrmanagerActions from '../+state/content-hrmanager.actions'; // 2. constructor中注入action$ constructor(private hrManagerFacade: ContentHrmanagerFacade, private action$: Actions) { // 从action$流中筛选错误的action this.action$.pipe( ofType(ContentHrmanagerActions.loadContentHrmanagerFailure)).subscribe(err => { console.log('err => ' + err.error); }); } |
当我们断开mock的话,在控制台打印错误信息。

按照ID查询数据¶
- 在actions.ts中追加用户查询的单个对象的action
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // content-hrmanager.actions.ts /** * 通过id查询 */ export const loadContentHrmanagerById = createAction( '[ContentHrmanager] Load ContentHrmanager by id', props<{ id: number }>() ); /** * 查询成功,返回所查用户数据 */ export const loadContentHrmanagerByIdSuccess = createAction( '[ContentHrmanager] Load ContentHrmanager Success by id', props<{ id: number, contentHrmanager: ContentHrmanagerEntity }>() ); |
- 在reducer.ts中追加分支条件
1 2 3 4 5 6 7 8 9 10 11 12 | // content-hrmanger.reducer.ts // 查询成功, 向store中存储最新的状态数据 on( ContentHrmanagerActions.loadContentHrmanagerByIdSuccess, (state, { id, contentHrmanager }) => contentHrmanagerAdapter.setOne(contentHrmanager, { ...state, loaded: true, selectedId: id }) ), |
- 在effects.ts中添加通过Id来查询用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * 通过id查询用户数据 */ loadContentHrmanagerById$ = createEffect(() => this.dataPersistence.fetch( ContentHrmanagerActions.loadContentHrmanagerById, { run: (action: any) => { // 取出参数id,查询用户数据 return this.hrManagerService.get(action.id).pipe( // 正常,发出成功action map(data => (ContentHrmanagerActions.loadContentHrmanagerByIdSuccess({ id: action.id, contentHrmanager: data}))), // 异常,发出失败action catchError(err => of(ContentHrmanagerActions.loadContentHrmanagerFailure({ error: (err as HttpErrorResponse).message })) ) ); }, onError: (action, error) => { console.error('Error', error); return ContentHrmanagerActions.loadContentHrmanagerFailure({ error }); } } ) ) |
- selectors.ts中利用既有的通过id取得数据的方法
1 2 3 4 5 | export const getSelected = createSelector( getContentHrmanagerEntities, getSelectedId, (entities, selectedId) => selectedId && entities[selectedId] ); |
- facade.ts中利用既有的方法查询用户数据
1 2 3 | selectedContentHrmanager$ = this.store.pipe( select(ContentHrmanagerSelectors.getSelected) ); |
- component中改造如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // form.component.ts /** * 用户 */ user$: Observable<ContentHrmanagerEntity>; // search方法改造 const id = +this.id.nativeElement.value; // 根据id获取用户信息 this.user$ = this.hrManagerFacade.selectedContentHrmanager$; // 发出加载全部用户数据action this.hrManagerFacade.dispatch(ContentHrmanagerActions.loadContentHrmanagerById({id: id})); // form.component.html页面下方添加如下代码: <div> <label>{{(user$ | async)?.id}} -- </label> <label>{{(user$ | async)?.name}} --</label> <label>{{(user$ | async)?.age}}</label> </div> |
上面做完后,我们在浏览器中测试结果如下:
