本篇要做什么
本文将使用effect从http api加载数据来显示,user-list.component改用action对象发布事件
github代码:https://github.com/shyandsy/angular-9-ngrx-user-mamagement
原教程视频连接
步骤
- 新建src\app\app.models.ts
通用的抽象response接口
export interface Response<T> {
code: number,
msg: string,
data: T
}
- 实现UserService, 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 customersUrl = "http://localhost:8000/user";
constructor(private http: HttpClient) {}
// http接口返回 {code: 0, messgae: "...", data: [{id: 1, username: "...", ...}, ....]}
// 直接映射到Response<User[]>
getUsers(): Observable<Response<User[]>> {
return this.http.get<Response<User[]>>(this.customersUrl);
}
}
- 添加src\app\users\state\user.effects.ts
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
){}
@Effect()
loadUsers$: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
ofType<userActions.LoadUsers>(
userActions.UserActionTypes.LOAD_USERS
),
mergeMap((actions: userActions.LoadUsers) =>
this.userService.getUsers().pipe(
map(
// 解析Response<User[]>, 如果code是0(成功),提取data中的User[]
(usersResponse: Response<User[]>) => {
if(usersResponse.code == 0){
return new userActions.LoadUsersSuccess(usersResponse.data);
}else{
return new userActions.LoadUsersFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.LoadUsersFailed(err)))
)
)
)
});
}
- 修改src\app\users\state\user.reducer.t
import * as userAction from './user.actions';
// 使用selector
import {createFeatureSelector, createSelector, Store} from '@ngrx/store';
import * as fromRoot from '../../state/app-state';
import { User } from '../user.model';
export interface UserState{
users: User[],
loading: boolean,
loaded: boolean,
error: string,
}
export interface AppState extends fromRoot.AppState{
users: UserState;
}
export const initialState: UserState= {
users: [],
loading: false,
loaded: true,
error: "",
}
export function userReducer(state = initialState, action: userAction.ACTION): UserState{
switch(action.type){
case userAction.UserActionTypes.LOAD_USERS:{
return {
...state,
loading: true,
}
}
case userAction.UserActionTypes.LOAD_USERS_SUCESS:{
return {
...state,
loading: false,
loaded: true,
users: action.payload
}
}
case userAction.UserActionTypes.LOAD_USERS_FAILED:{
return {
...state,
loading: true,
loaded: false,
error: action.payload
}
}
default: {
return state;
}
}
}
// 加入以下代码,创建getUserFeatureState
const getUserFeatureState = createFeatureSelector<UserState>(
"users"
)
// 创建selecotor getUsers
export const getUsers = createSelector(
getUserFeatureState,
(state: UserState) => state.users
)
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
)
- 修改src\app\users\users.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from "@angular/router";
// 加入EffectsModule, Actions
import { EffectsModule, Actions } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { userReducer } from './state/user.reducer';
// 加入UserEffect
import { UserEffect } from './state/user.effects';
import { UserComponent } from './user/user.component';
import { UserAddComponent } from './user-add/user-add.component';
import { UserEditComponent } from './user-edit/user-edit.component';
import { UserListComponent } from './user-list/user-list.component';
const userRoutes: Routes = [
{path:"", component: UserComponent,},
//{path:"/users/add", component: UserAddComponent}
];
@NgModule({
declarations: [
UserComponent,
UserAddComponent,
UserEditComponent,
UserListComponent
],
imports: [
CommonModule,
RouterModule.forChild(userRoutes),
StoreModule.forFeature("users", userReducer),
// 使用UserEffect
EffectsModule.forFeature([UserEffect])
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
],
exports:[
UserAddComponent,
UserEditComponent,
UserListComponent
]
})
export class UsersModule { }
- 修改src\app\app.module.ts,加入HttpCientModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { HttpClientModule } from '@angular/common/http'; // 加入
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { EffectsModule } from '@ngrx/effects'; // 加入
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { NavbarComponent } from './navbar/navbar.component';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NavbarComponent,
],
imports: [
BrowserModule,
StoreModule.forRoot({}), // 在module中添加reducer
StoreDevtoolsModule.instrument(),
EffectsModule.forRoot([]), // 加入
HttpClientModule, // 加入
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule { }
- 修改src\app\users\user-list\user-list.component.ts,加载数据
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { THIS_EXPR } from '@angular/compiler/src/output/output_ast';
// 使用action对象
import * as userActions from '../state/user.actions';
import {User} from './../user.model';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
styleUrls: ['./user-list.component.css']
})
export class UserListComponent implements OnInit {
users: User[];
constructor(private store: Store<any>) { }
ngOnInit(): void {
// 这行改为通过action对象发布事件
this.store.dispatch(new userActions.LoadUsers());
this.store.subscribe(state => (this.users = state.users.users));
}
}
- 修改src\app\users\user-list\user-list.component.html
<tr *ngFor="let user of (users$ | async)">
<th scope="row">{{user.username}}</th>
<td>{{user.role_id}}</td>
<td>{{user.cnname}}</td>
<td>{{user.enname}}</td>
<td>{{user.email}}</td>
<td>{{user.telephone}}</td>
<td>{{user.mobile}}</td>
<td>{{user.fax}}</td>
<td>{{user.address}}</td>
<td>{{user.post}}</td>
<td>{{user.status}}</td>
<th>
<a>修改</a>
<br>
<a>删除</a>
</th>
</tr>
效果
遗留问题
文件:src\app\users\state\user.effects.ts
effect发起了两次userActions.LoadUsers,产生了两次LoadUsersSuccess
暂时不清楚产生原因
@Effect()
loadUsers$: Observable<Action> = createEffect(() => {
// 执行了一次
console.log(111);
return this.actions$.pipe(
ofType<userActions.LoadUsers>(
userActions.UserActionTypes.LOAD_USERS
),
mergeMap((actions: userActions.LoadUsers) => {
// 执行了两次!!!!
console.log(actions);
return this.userService.getUsers().pipe(
map(
(usersResponse: Response<User[]>) => {
if(usersResponse.code == 0){
return new userActions.LoadUsersSuccess(usersResponse.data);
}else{
return new userActions.LoadUsersFailed(usersResponse.msg);
}
}
),
catchError(err => of(new userActions.LoadUsersFailed(err)))
)
})
)
});
2020-05-26补充1:
LoadUserSuccess产生两次的原因: creareEffect()和@Effect标注使用一个就好,两个一起使用就会产生两个一样的effect,最终导致UseLoginSuccess action生成两次。
effect的两种写法
// 第一种
loadUsers$: Observable<Action> = createEffect(() => {
return this.actions$.pipe(
....
);
});
// 第二种
@Effect()
loadUsers$: Observable<Action> = this.actions$.pipe(
return this.actions$.pipe(
....
);
});
2020-05-26补充2:
要在module中使用reducer和effect,需要把用到的reducer和effect加入module的import中。
比如我们需要在setting module(本例子中没有)中使用Role
@NgModule({
.......
imports: [
CommonModule,
RouterModule.forChild(userRoutes),
StoreModule.forFeature('users', userReducer),
StoreModule.forFeature('roles', settingRoleReducer), // 加入role
EffectsModule.forFeature([
UserEffect,
SettingRoleEffect // 加入role effect
]),
FormsModule, ReactiveFormsModule
],
......
})
大佬,有ng打包速度,代码体积等优化方向的文章么?
没有server代码。。。差评