angular 17 中使用了许多新feature, 比如standalone component,这带来了angular附属中间件比如状态管理中间件ngrx使用的改变。在翻阅ngrx文档后,发现即使最新版ngrx文档中仍然存在ngmodule与standalone写法的混合,容易造成使用者特别是最近版angluar 17 使用者的困惑。经过笔者实践,于是有的这篇博文用于简化说明在angular 17中ngrx的基本用法。
ngrx简单介绍
在angular中,实现状态管理可以使用service+observabel订阅,也可以使用第三方的NGRX。NGRX设计参考了react的redux,也由action,reducer等部分组成。
大概流程就是组件component中触发action事件,action事件调用对应的reducer函数,改变state/store状态。然后组件中可以通过selector订阅状态,拿到实时更新的共享信息。
安装ngrx
ng add @ngrx/store@latest
不要npm安装!!!否则引入报错没provider我也不知道为啥
ngrx实战
本文将通过实现用户登陆信息的状态管理的例子阐述ngrx基本用法。
本例创建了一个多属性状态的应用,可用于储存并获取某一实体类的不同属性信息。
1.创建action
创建两个action事件:set用于更新用户状态,reset用于清除用户状态
./store/action.ts
import { createAction,props} from "@ngrx/store";
import { StateType } from "./reducer";
export enum ActionTypes{
Change='Change Data',
Reset='Reset Data'
}
export const Set = createAction("Set Data",props< StateType>())
export const Reset = createAction("Reset Data")
2.创建reducer
创建reducer处理函数,分别处理set和reset事件
需要定义statetype状态接口,initialState初始状态
./store/reducer.ts
import { createReducer, on } from "@ngrx/store";
import { Set, Reset } from "./action";
export interface StateType{
id:number,
username:string,
nickname:string
}
export const initialState:StateType={
id:-1,
username:"",
nickname:""
}
export const userReducer = createReducer(
initialState,
on(Set, (state:StateType,action) =>{
state={...action};
return state;
} ),
on(Reset, (state) => initialState)
)
3.在app.config.ts中注册reducer
在standalone模式中,引入中间件在app.config.ts中注册
provideState创建了名为state的feature状态(可以理解一个状态的副本,我也不知道为啥有state还要搞出一个feature的概念),使用userReducer进行管理。
import { provideStore,provideState } from '@ngrx/store';
import { userReducer } from './store/reducer';
export const appConfig: ApplicationConfig = {
providers: [
//inject ngrx
provideStore(),
provideState('user',userReducer)
]
};
4.创建selector
创建selector用于获取用户状态,注意createFeatureSelector()参数要与刚刚注册的feature名称对应
由于状态中有多个属性值(id,username,nickname),需要分别创建selector用于获取对应属性。否则在组件中拿到整个状态的observable对象不好分别获取对应属性
import { createFeatureSelector,createSelector } from "@ngrx/store";
import { StateType } from "./reducer";
export const selectUser=createFeatureSelector<StateType>('user');
export const selectUsername=createSelector(selectUser,(user:StateType)=>{
return user.username;
})
export const selectNickname=createSelector(selectUser,(user:StateType)=>{
return user.nickname;
})
export const selectId=createSelector(selectUser,(user:StateType)=>{
return user.id;
})
5.在登陆组件中设置状态
在登陆组件中使用dispatch触发set处理函数,改变用户状态
./app/login/login.component.ts
import { Store } from '@ngrx/store';
import { Set } from '../store/action';
import { StateType } from '../store/reducer';
export class LoginComponent {
onSubmit() {
this.store.dispatch(Set(res.data));
}
constructor(
private store:Store<UserState> ) {}
}
6.在组件中获取状态
./app/header/header.component.ts:使用selector获取对应状态的属性值
import { Store } from '@ngrx/store';
import { StateType } from '../store/reducer';
import { selectUsername } from '../store/selector';
import { AsyncPipe} from '@angular/common';
@Component({
selector: 'app-header',
standalone: true,
imports: [AsyncPipe],
templateUrl: './header.component.html',
styleUrl: './header.component.css'
})
export class HeaderComponent {
username$=this.store.select(selectUsername);
constructor(
private store:Store<StateType>,
) {
}
}
./app/header/header.component.html:使用asyc管道读取observable对象
{{username$ | async}}
如有纰漏,敬请指出。
Reference: