1. First step is creating action creator

Action name should be clear which page, which functionality, what is the action name

"[Load Courses Effect] All Courses Loaded",
"[Courses Resolver] Load All Courses"

Action

import { createAction, props } from "@ngrx/store";
import { Course } from "./model/course"; export const loadAllCourse = createAction(
"[Courses Resolver] Load All Courses"
); export const allCoursesLoaded = createAction(
"[Load Courses Effect] All Courses Loaded",
props<{ courses: Course[] }>()
);

action types:

To make actions easy to work with, can create action types: basic it is just a improt and re-export

// actions-types.ts

import * as CoursesAction from "./cuorses.actions";

export { CoursesAction };

Effects:

Using 'createEffect', first param is a function to catch the event stream, second param is the options, can set '{dispatch: false}' which mean no other dispath event for this effect.

import { Injectable } from "@angular/core";
import { Actions, ofType } from "@ngrx/effects";
import { createEffect } from "@ngrx/effects";
import { CoursesAction } from "./actions-types";
import { CoursesHttpService } from "./services/courses-http.service";
import { concatMap, map, tap } from "rxjs/operators"; @Injectable()
export class CoursesEffects {
constructor(
private action$: Actions,
private coursesHttpService: CoursesHttpService
) {} loadAllCourses$ = createEffect(() =>
this.action$.pipe(
ofType(CoursesAction.loadAllCourse),
concatMap(() => this.coursesHttpService.findAllCourses()),
map(courses => CoursesAction.allCoursesLoaded({ courses }))
)
);
}
import { Injectable } from "@angular/core";
import { Router } from "@angular/router";
import { Actions, ofType, createEffect } from "@ngrx/effects";
import { AuthActions } from "./action-types"; import { tap } from "rxjs/operators"; @Injectable()
export class AuthEffects {
constructor(private action$: Actions, private router: Router) {} login$ = createEffect(
() =>
this.action$.pipe(
ofType(AuthActions.login),
tap(action => {
localStorage.setItem("user", JSON.stringify(action.user));
})
),
{ dispatch: false }
); logout$ = createEffect(
() =>
this.action$.pipe(
ofType(AuthActions.logout),
tap(action => localStorage.removeItem("user")),
tap(() => this.router.navigateByUrl("/"))
),
{ dispatch: false }
);
}

Register effect:

@NgModule({
imports: [
...
EffectsModule.forFeature([AuthEffects])
],
...
})

Reducers:

Reducer mainly take care five things:

1. Define state interface

2. Creating adapter

3. Generate intialize state

4. Generate next state for one action

5. Export selector

1. Initial state:

First we need to have the interface defined for the state:

/*
export interface CoursesState {
entities: { [key: number]: Course };
ids: number[];
}*/

NgRx provide API to create state interface:

import { EntityState, createEntityAdapter } from "@ngrx/entity";

export interface CoursesState extends EntityState<Course> {
/**Extend the entity here */
allCoursesLoaded: boolean;
}

'EntityState' contains 'entities' & 'ids'.

And we can extends interface by providing extra props: for example: 'allCoursesLoaded'.

2. Create Adapter:

export const adapter = createEntityAdapter<Course>({
sortComparer: compareCourses
// selectId: course => course.id // NgRx use 'id' by default
});

3. Generate initial state:

We can use 'adapter' to create initialstate

export const initCoursesState = adapter.getInitialState({
allCoursesLoaded: false
});

4. Create Reducer:

export const coursesReducer = createReducer(
initCoursesState,
on(CoursesAction.allCoursesLoaded, (state, action) =>
adapter.addAll
(action.courses, { ...state, allCoursesLoaded: true }) // next state
)
);

5. Export selector:

export const { selectAll } = adapter.getSelectors();

Full code:

import { Course, compareCourses } from "../model/course";
import { EntityState, createEntityAdapter } from "@ngrx/entity";
import { createReducer, on } from "@ngrx/store";
import { CoursesAction } from "../actions-types";
/*
export interface CoursesState {
entities: { [key: number]: Course };
ids: number[];
}*/ export interface CoursesState extends EntityState<Course> {
/**Extend the entity here */
allCoursesLoaded: boolean;
} export const adapter = createEntityAdapter<Course>({
sortComparer: compareCourses
// selectId: course => course.id // NgRx use 'id' by default
}); export const initCoursesState = adapter.getInitialState({
allCoursesLoaded: false
}); export const coursesReducer = createReducer(
initCoursesState,
on(CoursesAction.allCoursesLoaded, (state, action) =>
adapter.addAll(action.courses, { ...state, allCoursesLoaded: true })
)
); export const { selectAll } = adapter.getSelectors();

5. Selector:

Selecotor mainly has two API: createFeatureSelector & createSelector:

import { createSelector, createFeatureSelector } from "@ngrx/store";
import * as fromCourses from "./reducers/courses.reducers"; export const selectCoursesState = createFeatureSelector<
fromCourses.CoursesState
>("courses"
); export const selectAllCourses = createSelector(
selectCoursesState,
fromCourses.selectAll
); export const selectAllCoursesLoaded = createSelector(
selectCoursesState,
state => state.allCoursesLoaded
); export const selectBeginnerCourses = createSelector(
selectAllCourses,
courses => courses.filter(course => course.category === "BEGINNER")
); export const selectAdvancedCourses = createSelector(
selectAllCourses,
courses => courses.filter(course => course.category === "ADVANCED")
); export const selectPromoTotal = createSelector(
selectAllCourses,
courses => courses.filter(course => course.promo).length
);

Component:

import { Component, OnInit } from "@angular/core";
import { compareCourses, Course } from "../model/course";
import { Observable } from "rxjs";
import { defaultDialogConfig } from "../shared/default-dialog-config";
import { EditCourseDialogComponent } from "../edit-course-dialog/edit-course-dialog.component";
import { MatDialog } from "@angular/material";
import { Store, select } from "@ngrx/store";
import { AppState } from "../../reducers";
import * as coursesSelector from "../courses.selectors"; @Component({
selector: "home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.css"]
})
export class HomeComponent implements OnInit {
promoTotal$: Observable<number>; beginnerCourses$: Observable<Course[]>; advancedCourses$: Observable<Course[]>; constructor(private dialog: MatDialog, private store: Store<AppState>) {} ngOnInit() {
this.reload();
} reload() {
this.beginnerCourses$ = this.store.pipe(
select(coursesSelector.selectBeginnerCourses)
); this.advancedCourses$ = this.store.pipe(
select(coursesSelector.selectAdvancedCourses)
); this.promoTotal$ = this.store.pipe(
select(coursesSelector.selectPromoTotal)
);
} onAddCourse() {
const dialogConfig = defaultDialogConfig(); dialogConfig.data = {
dialogTitle: "Create Course",
mode: "create"
}; this.dialog.open(EditCourseDialogComponent, dialogConfig);
}
}

select:

'select' operator can be used in many places, mainly if you want to get piece of data from store, for example, it can be used in resolver as well:

import { Injectable } from "@angular/core";
import {
Resolve,
ActivatedRouteSnapshot,
RouterStateSnapshot
} from "@angular/router";
import { Observable } from "rxjs";
import { Store, select } from "@ngrx/store";
import { AppState } from "../reducers";
import { CoursesAction } from "./actions-types";
import { tap, first, finalize, filter } from "rxjs/operators";
import { adapter } from "./reducers/courses.reducers";
import { selectAllCoursesLoaded } from "./courses.selectors"; @Injectable()
export class CoursesResolver implements Resolve<any> {
loading = false;
constructor(private store: Store<AppState>) {} resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> {
return this.store.pipe(
select(selectAllCoursesLoaded),
tap(courseLoaded => {
if (!this.loading && !courseLoaded) {
this.loading = true;
this.store.dispatch(CoursesAction.loadAllCourse());
}
}),
// this resolve need to complete, so we can use first()
filter(courseLoaded => courseLoaded),
first(),
finalize(() => (this.loading = false))
);
}
}

最新文章

  1. 让姑姑不再划拳 码农也要有原则 : SOLID via C#
  2. 两个坑-Linux下Network-Manager有线未托管-DNS resolv.conf文件开机被清空
  3. SLP的模块结构
  4. Linux下who命令之C语言实现
  5. CSS 样式使用
  6. dubbo 转
  7. android上传文件到服务器
  8. 06MySQL数据库入门
  9. Java 后台sql注入
  10. 2017了,回家前 &quot;年末&quot; 分享:下雨,飘雪,红包雨,碰撞球,自定义View
  11. 快速排序/快速查找(第k个, 前k个问题)
  12. imooc网的主体框架
  13. [BOI2004]Sequence 数字序列(左偏树)
  14. Linux安装RocketMQ
  15. eslint prettier editrorconfig - 写出干净的前端代码
  16. 【iCore4 双核心板_ARM】例程二十四:LWIP_DHCP实验——动态分配IP地址
  17. python——位运算之进制转化
  18. MongoDB登录验证及用户管理
  19. STM32示波器 信号发生器
  20. 运行Delphi XE10的MongoDB例程,测试Delphi插入记录性能

热门文章

  1. go build、go install、go get命令详解
  2. 协议——SPI
  3. Linux中添加用户与删除用户
  4. LOJ2482 CEOI2017 Mousetrap 二分答案、树形DP
  5. js加减乘除函数
  6. python 基础(实例1——登陆小游戏)
  7. 【转载】C#中List集合使用Last方法获取最后一个元素
  8. python3基础之“小练习(2)”
  9. Swift 4 中的泛型
  10. sqlserver 将一个表中的某些字段更新到另一个表中(转载)