本篇要做什么
本文将使用effect entity来实现CRUD操作,需要用到ReactiveForm
github代码:https://github.com/shyandsy/angular-9-ngrx-user-mamagement
原教程视频连接
步骤
- src/app/app.module.ts导入ReactiveFormsModule
不在给出全部代码,需要的话,参照前一篇文章。
// 导入ReactiveFormsModule
import { ReactiveFormsModule } from '@angular/forms';
imports: [
BrowserModule,
StoreModule.forRoot({}),
StoreDevtoolsModule.instrument(),
EffectsModule.forRoot([]),
HttpClientModule,
AppRoutingModule,
ReactiveFormsModule, // 加入ReactiveFormsModule,使用FormBuilder需要导入ReactiveFormsModule
],
- 给出完整的src/app/users/user.service.ts,提供增删改查功能
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { Response } from './../app.models';
import { User, } from "./user.model";
@Injectable({
providedIn: "root"
})
export class UserService {
private usersUrl = "http://localhost:8000/user";
constructor(private http: HttpClient) {}
getUsers(): Observable<Response<User[]>> {
console.log("user service: Get Users")
return this.http.get<Response<User[]>>(this.usersUrl,
//{headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}}
);
}
getUserById(payload: number): Observable<Response<User>> {
console.log("user service: Get User By Id")
return this.http.get<Response<User>>(`${this.usersUrl}/${payload}`,
//{headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}}
);
}
createUser(payload: User): Observable<Response<User>> {
console.log("user service: Create User")
console.log(payload)
return this.http.post<Response<User>>(this.usersUrl,
payload,
//{headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}}
);
}
updateUser(user: User): Observable<Response<User>> {
console.log("user service: Update User")
return this.http.patch<Response<User>>(
`${this.usersUrl}/${user.id}`,
user,
//{headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}}
);
}
deleteUser(payload: number) {
console.log("user service: Delete User")
return this.http.delete(`${this.usersUrl}/${payload}`,
//{headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}}
);
}
}
- src/app/users/users.module.ts导入ReactiveForm
// 加入
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
imports: [
CommonModule,
RouterModule.forChild(userRoutes),
StoreModule.forFeature("users", userReducer),
EffectsModule.forFeature([UserEffect]),
// 加入这行
FormsModule, ReactiveFormsModule
],
- 修改user/state中的actions,effects,reducer
src\app\users\state\user.actions.ts
import { Action } from '@ngrx/store';
import { Update } from '@ngrx/entity';
import { User } from '../user.model';
export enum UserActionTypes {
LOAD_USERS = "[User] Load Users",
LOAD_USERS_SUCESS = "[User] Load Users Success",
LOAD_USERS_FAILED = "[User] Load Users Failed",
// 增加单个user load操作
LOAD_USER = "[User] Load User",
LOAD_USER_SUCESS = "[User] Load User Success",
LOAD_USER_FAILED = "[User] Load User Failed",
// 增加create user
CREATE_USER = "[User] Create User",
CREATE_USER_SUCESS = "[User] Create User Success",
CREATE_USER_FAILED = "[User] Create User Failed",
// 增加update user
UPDATE_USER = "[User] Update User",
UPDATE_USER_SUCESS = "[User] Update User Success",
UPDATE_USER_FAILED = "[User] Update User Failed",
// 增加delete user
DELETE_USER = "[User] Delete User",
DELETE_USER_SUCESS = "[User] Delete User Success",
DELETE_USER_FAILED = "[User] Delete User Failed",
}
export class LoadUsers implements Action{
readonly type = UserActionTypes.LOAD_USERS;
}
export class LoadUsersSuccess implements Action{
readonly type = UserActionTypes.LOAD_USERS_SUCESS;
constructor(public payload: User[]) {}
}
export class LoadUsersFailed implements Action{
readonly type = UserActionTypes.LOAD_USERS_FAILED;
constructor(public payload: string) {}
}
// 增加加载单个user,编辑时候加载user
export class LoadUser implements Action{
readonly type = UserActionTypes.LOAD_USER;
constructor(public payload: number) {} // 使用id来加载
}
export class LoadUserSuccess implements Action{
readonly type = UserActionTypes.LOAD_USER_SUCESS;
constructor(public payload: User) {}
}
export class LoadUserFailed implements Action{
readonly type = UserActionTypes.LOAD_USER_FAILED;
constructor(public payload: string) {}
}
// 增加update user
export class UpdateUser implements Action{
readonly type = UserActionTypes.UPDATE_USER;
constructor(public payload: User) {} // 提交user对象
}
export class UpdateUserSuccess implements Action{
readonly type = UserActionTypes.UPDATE_USER_SUCESS;
constructor(public payload: Update<User>) {} // 使用ngrx/entity的Update
}
export class UpdateUserFailed implements Action{
readonly type = UserActionTypes.UPDATE_USER_FAILED;
constructor(public payload: string) {}
}
// 增加create user
export class CreateUser implements Action{
readonly type = UserActionTypes.CREATE_USER;
constructor(public payload: User) {}
}
export class CreateUserSuccess implements Action{
readonly type = UserActionTypes.CREATE_USER_SUCESS;
constructor(public payload: User) {}
}
export class CreateUserFailed implements Action{
readonly type = UserActionTypes.CREATE_USER_FAILED;
constructor(public payload: string) {}
}
// 增加delete user
export class DeleteUser implements Action{
readonly type = UserActionTypes.DELETE_USER;
constructor(public payload: number) {} // 使用id来删除对象
}
export class DeleteUserSuccess implements Action{
readonly type = UserActionTypes.DELETE_USER_SUCESS;
constructor(public payload: number) {} // 返回id
}
export class DeleteUserFailed implements Action{
readonly type = UserActionTypes.DELETE_USER_FAILED;
constructor(public payload: string) {}
}
// 增加的action加入到这里
export type ACTION = LoadUsers | LoadUsersSuccess | LoadUsersFailed |
LoadUser | LoadUserSuccess | LoadUserFailed |
UpdateUser | UpdateUserSuccess | UpdateUserFailed |
CreateUser | CreateUserSuccess | CreateUserFailed |
DeleteUser | DeleteUserSuccess | DeleteUserFailed
;
src\app\users\state\user.effects.ts
这个文件重点说两点
- 方法前面加@Effect()会导致方法执行两次,就是前面遗留的问题终于搞清楚了。
- 我的api返回的是{code: 0, message: "", data:[]},code=0表示成功,message是错误消息,data是返回的数据。所以我要在response中判断response.code来决定是发出Success Action还是Fail Action
//@Effect() => 如果这行不注释掉,action会调用两次,具体原因不清楚
loadUsers$: Observable<Action> = createEffect(() => {
.......
}
``
return this.userService.getUsers().pipe(
map(
(usersResponse: Response<User[]>) => {
// 判断api请求是否成功
if(usersResponse.code == 0){
return new userActions.LoadUsersSuccess(usersResponse.data);
}else{
return new userActions.LoadUsersFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.LoadUsersFailed(err))))
完整代码
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType, createEffect } from '@ngrx/effects';
import { Action } from '@ngrx/store';
import { Observable, of } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { UserService } from '../user.service';
import * as userActions from '../state/user.actions';
import { Response } from './../../app.models';
import { User } from '../user.model';
@Injectable()
export class UserEffect{
constructor(
private actions$: Actions,
private userService: UserService
){}
loadUsers$: Observable<Action> = createEffect(() => {
console.log("test111");
console.log(this.actions$)
return this.actions$.pipe(
ofType<userActions.LoadUsers>(
userActions.UserActionTypes.LOAD_USERS
),
mergeMap((actions: userActions.LoadUsers) => {
console.log("xxx111")
console.log(actions)
return this.userService.getUsers().pipe(
map(
(usersResponse: Response<User[]>) => {
console.log("xxx2222");
if(usersResponse.code == 0){
return new userActions.LoadUsersSuccess(usersResponse.data);
}else{
return new userActions.LoadUsersFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.LoadUsersFailed(err)))
)
})
)
});
// 加载一个user,用于编辑
loadUser: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType<userActions.LoadUser>(
userActions.UserActionTypes.LOAD_USER
),
mergeMap((action: userActions.LoadUser) => {
console.log(action);
return this.userService.getUserById(action.payload).pipe(
map(
(usersResponse: Response<User>) => {
if(usersResponse.code == 0){
return new userActions.LoadUserSuccess(usersResponse.data);
}else{
return new userActions.LoadUserFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.LoadUserFailed(err)))
)
})
)
});
// 创建一个user
createUser: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType<userActions.CreateUser>(
userActions.UserActionTypes.CREATE_USER
),
map((action: userActions.CreateUser) => action.payload),
mergeMap((user: User) => {
console.log(user);
return this.userService.createUser(user).pipe(
map(
(usersResponse: Response<User>) => {
if(usersResponse.code == 0){
return new userActions.CreateUserSuccess(usersResponse.data);
}else{
return new userActions.CreateUserFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.CreateUserFailed(err)))
)
})
)
});
// 更新一个user
updateUser: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType<userActions.UpdateUser>(
userActions.UserActionTypes.UPDATE_USER
),
map((action: userActions.UpdateUser) => action.payload),
mergeMap((user: User) => {
console.log(user);
return this.userService.updateUser(user).pipe(
map(
(usersResponse: Response<User>) => {
if(usersResponse.code == 0){
return new userActions.UpdateUserSuccess({
id: usersResponse.data.id,
changes: usersResponse.data
});
}else{
return new userActions.UpdateUserFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.UpdateUserFailed(err)))
)
})
)
});
// 删除一个user
deletUser: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType<userActions.DeleteUser>(
userActions.UserActionTypes.DELETE_USER
),
map((action: userActions.DeleteUser) => action.payload),
mergeMap((id: number) => {
console.log(id);
return this.userService.deleteUser(id).pipe(
map(
(usersResponse: Response<User>) => {
if(usersResponse.code == 0){
return new userActions.DeleteUserSuccess(id)
}else{
return new userActions.DeleteUserFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.DeleteUserFailed(err)))
)
})
)
});
}
修改src\app\users\state\user.reducer.ts
- 不在处理LOAD_USERS,LOAD_USER, CREATE_USER, UPDATE_USER, DELETE_USER,这些都是网络api调用,现在交给user.effects.ts去处理
- reducer专注处理业务,处理各种网络操作的成功和失败。LOAD_USERS_SUCESS,LOAD_USERS_FAILED,LOAD_USER_SUCESS,LOAD_USER_FAILED,CREATE_USER_SUCESS,CREATE_USER_FAILED,UPDATE_USER_SUCESS,UPDATE_USER_FAILED,DELETE_USER_SUCESS,DELETE_USER_FAILED
-
import * as userAction from './user.actions';
import {createFeatureSelector, createSelector, Store} from '@ngrx/store';
import {EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import * as fromRoot from '../../state/app-state';
import { User } from '../user.model';
export interface UserState extends EntityState
selectedUserId: number | null;
loading: boolean,
loaded: boolean,
error: string,
}
export interface AppState extends fromRoot.AppState{
users: UserState;
}
export const userAdapter: EntityAdapter
export const defaultUser: UserState = {
ids: [],
entities: {},
selectedUserId: null,
loading: false,
loaded: false,
error: ""
}
export const initialState: UserState = userAdapter.getInitialState(defaultUser);
export function userReducer(state = initialState, action: userAction.ACTION): UserState{
switch(action.type){
case userAction.UserActionTypes.LOAD_USERS_SUCESS:{
console.log("reducer: LOAD_USERS_SUCESS")
return userAdapter.addAll(action.payload, {
...state,
loading: false,
loaded: true,
})
}
case userAction.UserActionTypes.LOAD_USERS_FAILED:{
console.log("reducer: LOAD_USERS_FAILED")
return {
...state,
entities: {},
loading: true,
loaded: false,
error: action.payload
}
}
// load user
case userAction.UserActionTypes.LOAD_USER_SUCESS:{
console.log("reducer: LOAD_USER_SUCESS")
return userAdapter.addOne(action.payload, {
...state,
selectedUserId: action.payload.id,
})
}
case userAction.UserActionTypes.LOAD_USER_FAILED:{
console.log("reducer: LOAD_USER_FAILED")
return {
...state,
error: action.payload,
}
}
// create user
case userAction.UserActionTypes.CREATE_USER_SUCESS:{
console.log("reducer: CREATE_USER_SUCESS")
return userAdapter.addOne(action.payload, state)
}
case userAction.UserActionTypes.CREATE_USER_FAILED:{
console.log("reducer: CREATE_USER_FAILED")
return {
...state,
error: action.payload,
}
}
// update user
case userAction.UserActionTypes.UPDATE_USER_SUCESS:{
console.log("reducer: UPDATE_USER_SUCESS")
return userAdapter.updateOne(action.payload, state)
}
case userAction.UserActionTypes.UPDATE_USER_FAILED:{
console.log("reducer: UPDATE_USER_FAILED")
return {
...state,
error: action.payload,
}
}
// delete user
case userAction.UserActionTypes.DELETE_USER_SUCESS:{
console.log("reducer: DELETE_USER_SUCESS")
return userAdapter.removeOne(action.payload, state)
}
case userAction.UserActionTypes.DELETE_USER_FAILED:{
console.log("reducer: DELETE_USER_FAILED")
return {
...state,
error: action.payload,
}
}
default: {
return state;
}
}
}
const getUserFeatureState = createFeatureSelector
"users"
)
export const getUsers = createSelector(
getUserFeatureState,
userAdapter.getSelectors().selectAll
)
export const getUsersLoading = createSelector(
getUserFeatureState,
(state: UserState) => state.loading
)
export const getUsersLoaded = createSelector(
getUserFeatureState,
(state: UserState) => state.loaded
)
export const getError = createSelector(
getUserFeatureState,
(state: UserState) => state.error
)
// 增加获取当前选中user的id
export const getCurrentUserId = createSelector(
getUserFeatureState,
(state: UserState) => state.selectedUserId
)
// 增加获取当前选中user
export const getCurrentUser = createSelector(
getUserFeatureState,
getCurrentUserId,
state => state.entities[state.selectedUserId]
)
5. 修改src/app/users/user-add组件
修改模板src\app\users\user-add\user-add.component.html,
- 使用reactiveForm,用户点击提交,调用component的createUser()方法,
- createUser方法在user-add.component.ts中实现这个方法
添加用户
<form [formGroup]="userForm" (ngSubmit)="createUser()" class="form-inline mb-4">
..........
<button type="submit" class="btn btn-primary mb-2">添加</button>
修改src\app\users\user-add\user-add.component.ts
- 原视频中form使用了下面这段代码创建form,这种做法已经不适用于angular 9。新的做法请参考我下面的代码
this.customerForm = this.fb.group({
name: ["", Validators.required], // angular 9这么做会报错
});
- 原视频中从form获取数据的方式,也已经不再适用于angular 9。新的做法请参考我下面的代码
const newCustomer: Customer = {
name: this.customerForm.get("name").value, // angular 9这么做会报错
.......
}
完整代码
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import as userActions from '../state/user.actions';
import as fromUser from '../state/user.reducer';
import { User } from '../user.model';
@Component({
selector: 'app-user-add',
templateUrl: './user-add.component.html',
styleUrls: ['./user-add.component.css']
})
export class UserAddComponent implements OnInit {
userForm: FormGroup;
constructor(
private fb: FormBuilder, // inject FormBuilder
private store: Store
) {
}
ngOnInit(): void {
this.userForm = this.fb.group({
username: new FormControl("", {validators: [Validators.required,]}),
password: new FormControl("", {validators: [Validators.minLength(6), Validators.maxLength(32)]}),
role_id: new FormControl("", {validators: [Validators.required]}),
cnname: new FormControl("", {validators: [Validators.minLength(2), Validators.maxLength(6)]}),
enname: new FormControl("", {validators: [Validators.minLength(2), Validators.maxLength(30)]}),
email: new FormControl("", {validators: [Validators.email]}),
telephone: new FormControl("", {validators:[Validators.minLength(10), Validators.maxLength(11)]}),
mobile: new FormControl("", {validators:[Validators.minLength(10), Validators.maxLength(11)]}),
fax: new FormControl("", {validators:[Validators.minLength(10), Validators.maxLength(11)]}),
address: new FormControl("", {validators:[Validators.minLength(3), Validators.maxLength(50)]}),
post: new FormControl("", {validators:[Validators.minLength(6), Validators.maxLength(8)]}),
expired: new FormControl("", {validators:[Validators.minLength(19), Validators.maxLength(19)]}),
status: new FormControl("", {validators:[Validators.min(0), Validators.max(1)]}),
})
}
// 创建用户
createUser(){
console.log(this.userForm.value)
console.log("创建用户")
// 构造user对象
const newUser: User = {
username: this.userForm.value.username,
password: this.userForm.value.password,
role_id: this.userForm.value.role_id,
cnname: this.userForm.value.cnname,
enname: this.userForm.value.enname,
email: this.userForm.value.email,
telephone: this.userForm.value.telephone,
mobile: this.userForm.value.mobile,
fax: this.userForm.value.fax,
address: this.userForm.value.address,
post: this.userForm.value.post,
status: this.userForm.value.status,
token: "",
ip: "",
expired: this.userForm.value.expired
};
// 发起create user action
this.store.dispatch(
new userActions.CreateUser(newUser)
)
// 清空表单
this.userForm.reset()
}
}
6. 修改src/app/users/user-edit组件
修改src\app\users\user-edit\user-edit.component.html
更新用户
修改src\app\users\user-edit\user-edit.component.ts
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import as userActions from '../state/user.actions';
import as fromUser from '../state/user.reducer';
import { User } from '../user.model';
import { Observable } from 'rxjs';
@Component({
selector: 'app-user-edit',
templateUrl: './user-edit.component.html',
styleUrls: ['./user-edit.component.css']
})
export class UserEditComponent implements OnInit {
userForm: FormGroup;
constructor(
private fb: FormBuilder, // inject FormBuilder
private store: Store
) { }
ngOnInit(): void {
// 创建表单, 注意id是null,这个id不在表单输出
this.userForm = this.fb.group({
id: null,
username: new FormControl("", {validators: [Validators.required,]}),
password: new FormControl("", {validators: [Validators.minLength(6), Validators.maxLength(32)]}),
role_id: new FormControl("", {validators: [Validators.required]}),
cnname: new FormControl("", {validators: [Validators.minLength(2), Validators.maxLength(6)]}),
enname: new FormControl("", {validators: [Validators.minLength(2), Validators.maxLength(30)]}),
email: new FormControl("", {validators: [Validators.email]}),
telephone: new FormControl("", {validators:[Validators.minLength(10), Validators.maxLength(11)]}),
mobile: new FormControl("", {validators:[Validators.minLength(10), Validators.maxLength(11)]}),
fax: new FormControl("", {validators:[Validators.minLength(10), Validators.maxLength(11)]}),
address: new FormControl("", {validators:[Validators.minLength(3), Validators.maxLength(50)]}),
post: new FormControl("", {validators:[Validators.minLength(6), Validators.maxLength(8)]}),
expired: new FormControl("", {validators:[Validators.minLength(19), Validators.maxLength(19)]}),
status: new FormControl("", {validators:[Validators.min(0), Validators.max(1)]}),
})
// 订阅当前选中的user
const user$: Observable<User> = this.store.select(
fromUser.getCurrentUser
)
// 如果当前选中的user有变化,更新表单
user$.subscribe(currentUser => {
if(currentUser){
console.log("user id := " + currentUser.id)
this.userForm.patchValue({
id: currentUser.id, // userForm存储user id
username: currentUser.username,
password: currentUser.password,
role_id: currentUser.role_id,
cnname: currentUser.cnname,
enname: currentUser.enname,
email: currentUser.email,
telephone: currentUser.telephone,
mobile: currentUser.mobile,
fax: currentUser.fax,
address: currentUser.address,
post: currentUser.post,
expired: currentUser.expired,
status: currentUser.status
})
}
})
}
updateUser(){
console.log(this.userForm.value)
console.log("更新用户")
console.log("user id := " + this.userForm.value.id)
// 构造user
const newUser: User = {
id: this.userForm.value.id,
username: this.userForm.value.username,
password: this.userForm.value.password,
role_id: this.userForm.value.role_id,
cnname: this.userForm.value.cnname,
enname: this.userForm.value.enname,
email: this.userForm.value.email,
telephone: this.userForm.value.telephone,
mobile: this.userForm.value.mobile,
fax: this.userForm.value.fax,
address: this.userForm.value.address,
post: this.userForm.value.post,
status: this.userForm.value.status,
token: "",
ip: "",
expired: this.userForm.value.expired
};
// 发出user update action
this.store.dispatch(
new userActions.UpdateUser(newUser)
)
// 清空表单
this.userForm.reset()
}
}
7. 修改src/app/users/user-list组件
修改src\app\users\user-list\user-list.component.html
- 加入修改user响应,editUser(user)
- 加入删除user响应,deleteUser(user)
Users
用户名 | 角色 | 名字 | 电话 | 手机 | 传真 | 地址 | 邮编 | 状态 | 操作 | |
---|---|---|---|---|---|---|---|---|---|---|
{{error}} | ||||||||||
{{user.username}} | {{user.email}} | {{user.role_id}} | {{user.cnname}}{{user.enname}} | {{user.telephone}} | {{user.mobile}} | {{user.fax}} | {{user.address}} | {{user.post}} | {{user.status}} |
修改
删除 |
修改src\app\users\user-list\user-list.component.ts
import { Component, OnInit } from '@angular/core';
import { Store,select } from '@ngrx/store';
import { Observable } from 'rxjs';
import { THIS_EXPR } from '@angular/compiler/src/output/output_ast';
import as userActions from '../state/user.actions';
import as fromUser from '../state/user.reducer';
import {User} from './../user.model';
import { compileNgModuleFromRender2 } from '@angular/compiler/src/render3/r3_module_compiler';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
users$: Observable<User[]>;
errors$: Observable
constructor(private store: Store
ngOnInit(): void {
this.store.dispatch(new userActions.LoadUsers());
// 调用reducer的getUsers方法
this.users$ = this.store.pipe(select(fromUser.getUsers));
//this.store.subscribe(state => (this.users = state.users.users));
}
// 编辑user
editUser(user: User){
this.store.dispatch(new userActions.LoadUser(user.id))
}
// 删除user
deleteUser(user:User){
if(confirm("确定删除用户?")){
this.store.dispatch(new userActions.DeleteUser(user.id))
}
}
}
### 效果
![11.png][1]
![12.png][2]
[1]: http://blog.shyclouds.net/usr/uploads/2020/05/3083320597.png
[2]: http://blog.shyclouds.net/usr/uploads/2020/05/4008506764.png