這篇文章帶大家深入了解angular的狀態管理器NgRx,介紹一下NgRx的使用方法,希望對大家有幫助!
NgRx 是Angular 應用中實作全域狀態管理的Redux 架構解決方案。 【相關教學推薦:《angular教學》】

@ngrx/store:全域狀態管理模組
@ngrx/effects:處理副作用
@ngrx/store-devtools:瀏覽器偵錯工具,需要依賴Redux Devtools Extension
@ngrx/schematics:命令列工具,快速產生NgRx 檔案
@ngrx/entity:提高開發者在Reducer 中操作資料的效率
@ngrx/router-store:將路由狀態同步到全域Store
1、下載NgRx
npm install @ngrx/store @ngrx/effects @ngrx/entity @ngrx/router-store @ngrx/store-devtools @ngrx/schematics
2、配置NgRx CLI
ng config cli.defaultCollection @ngrx/schematics
// angular.json
"cli": {
"defaultCollection": "@ngrx/schematics"
}3、創建Store
ng g store State --root --module app.module.ts --statePath store --stateInterface AppState
4.創建Action
ng g action store/actions/counter --skipTests
import { createAction } from "@ngrx/store"
export const increment = createAction("increment")
export const decrement = createAction("decrement")5、創建Reducer
ng g reducer store/reducers/counter --skipTests --reducers=../index.ts
import { createReducer, on } from "@ngrx/store"
import { decrement, increment } from "../actions/counter.actions"
export const counterFeatureKey = "counter"
export interface State {
count: number
}
export const initialState: State = {
count: 0
}
export const reducer = createReducer(
initialState,
on(increment, state => ({ count: state.count + 1 })),
on(decrement, state => ({ count: state.count - 1 }))
)6、創建Selector
ng g selector store/selectors/counter --skipTests
import { createFeatureSelector, createSelector } from "@ngrx/store"
import { counterFeatureKey, State } from "../reducers/counter.reducer"
import { AppState } from ".."
export const selectCounter = createFeatureSelector<AppState, State>(counterFeatureKey)
export const selectCount = createSelector(selectCounter, state => state.count)7.組件類別觸發Action、取得狀態
import { select, Store } from "@ngrx/store"
import { Observable } from "rxjs"
import { AppState } from "./store"
import { decrement, increment } from "./store/actions/counter.actions"
import { selectCount } from "./store/selectors/counter.selectors"
export class AppComponent {
count: Observable<number>
constructor(private store: Store<AppState>) {
this.count = this.store.pipe(select(selectCount))
}
increment() {
this.store.dispatch(increment())
}
decrement() {
this.store.dispatch(decrement())
}
}8.組件範本顯示狀態
<button (click)="increment()">+</button>
<span>{{ count | async }}</span>
<button (click)="decrement()">-</button>1.在元件中使用dispatch 觸發Action 時傳遞參數,參數最終會被放置在Action 物件中。
this.store.dispatch(increment({ count: 5 }))2、建立Action Creator 函數時,取得參數並指定參數類型。
import { createAction, props } from "@ngrx/store"
export const increment = createAction("increment", props<{ count: number }>())export declare function props<P extends object>(): Props<P>;
3.在Reducer 中透過Action 物件取得參數。
export const reducer = createReducer(
initialState,
on(increment, (state, action) => ({ count: state.count + action.count }))
)metaReducer 是Action -> Reducer 之間的鉤子,允許開發者對Action 進行預處理(在普通Reducer 函數呼叫之前呼叫)。
function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function (state, action) {
return reducer(state, action)
}
}
export const metaReducers: MetaReducer<AppState>[] = !environment.production
? [debug]
: []需求:在頁面中新增一個按鈕,點擊按鈕後延遲一秒鐘讓數值增加。
1.在元件模板中新增一個用於非同步數值增加的按鈕,按鈕被點擊後執行increment_async方法
<button (click)="increment_async()">async</button>
2、在元件類別中新增increment_async方法,並在方法中觸發執行非同步操作的Action
increment_async() {
this.store.dispatch(increment_async())
}3.在Action 檔中新增執行非同步操作的Action
export const increment_async = createAction("increment_async")4.建立Effect,接收Action 並執行副作用,繼續觸發Action
ng g effect store/effects/counter --root --module app.module.ts --skipTests
Effect 功能由@ngrx/effects 模組提供,所以在根模組中需要導入相關的模組依賴
import { Injectable } from "@angular/core"
import { Actions, createEffect, ofType } from "@ngrx/effects"
import { increment, increment_async } from "../actions/counter.actions"
import { mergeMap, map } from "rxjs/operators"
import { timer } from "rxjs"
// createEffect
// 用於建立 Effect, Effect 用於執行副作用.
// 呼叫方法時傳遞回呼函數, 回呼函數中傳回Observable 物件, 物件中要發出副作用執行完成後要觸發的Action 物件// 回呼函數的回傳值在createEffect 方法內部被繼續傳回, 最終傳回值儲存在了Effect 類別的屬性中// NgRx 在實例化Effect 類別後, 會訂閱Effect 類別屬性, 當副作用執行完成後它會取得到要觸發的Action 物件並觸發這個Action
// Actions
// 當元件觸發Action 時, Effect 需要透過Actions 服務接收Action, 所以在Effect 類別中透過constructor 建構子參數的方式將Actions 服務類別的實例物件注入到Effect 類別中// Actions 服務類別的實例物件為Observable物件, 當有Action 被觸發時, Action 物件本身會作為資料流被發出// ofType
// 篩選目標 Action 物件.
// 參數為目標 Action 的 Action Creator 函數// 如果未過濾出目標 Action 物件, 本次不會繼續傳送資料流// 如果篩選出目標 Action 物件, 會將 Action 物件作為資料流繼續發出 @Injectable()
export class CounterEffects {
constructor(private actions: Actions) {
// this.loadCount.subscribe(console.log)
}
loadCount = createEffect(() => {
return this.actions.pipe(
ofType(increment_async),
mergeMap(() => timer(1000).pipe(map(() => increment({ count: 10 }))))
)
})
}1、概述
Entity 譯為實體,實體就是集合中的一條資料。
NgRx 中提供了實體適配器對象,在實體適配器物件下方提供了各種操作集合中實體的方法,目的是提高開發者操作實體的效率。
2、核心
1、EntityState:實體類型介面
/*
{
ids: [1, 2],
entities: {
1: { id: 1, title: "Hello Angular" },
2: { id: 2, title: "Hello NgRx" }
}
}
*/
export interface State extends EntityState<Todo> {}2、createEntityAdapter: 建立實體適配器對象
3、EntityAdapter:實體適配器物件類型介面
export const adapter: EntityAdapter<Todo> = createEntityAdapter<Todo>()
// 取得初始狀態 可以傳遞物件參數 也可以不傳// {ids: [], entities: {}}
export const initialState: State = adapter.getInitialState()3、實例方法
https://ngrx.io/guide/entity/adapter#adapter-collection-methods
4、選擇器
// selectTotal 取得資料條數// selectAll 取得所有資料以陣列形式呈現// selectEntities 取得實體集合以字典形式呈現// selectIds 取得id集合, 以陣列形式呈現const { selectIds, selectEntities, selectAll, selectTotal } = adapterTo .getSelectors();export const selectTodo = createFeatureSelector<AppState, State>(todoFeatureKey) export const selectTodos = createSelector(selectTodo, selectAll)
1、同步路由狀態
1)引入模組
import { StoreRouterConnectingModule } 從 "@ngrx/router-store"
@NgModule({
imports: [
StoreRouterConnectingModule.forRoot()
]
})
export class AppModule {}2)將路由狀態整合到Store
import * as fromRouter from "@ngrx/router-store"
export interface AppState {
router: fromRouter.RouterReducerState
}
export const reducers: ActionReducerMap<AppState> = {
router: fromRouter.routerReducer
}2、建立獲取路由狀態的Selector
// router.selectors.ts
import { createFeatureSelector } from "@ngrx/store"
import { AppState } from ".."
import { RouterReducerState, getSelectors } from "@ngrx/router-store"
const selectRouter = createFeatureSelector<AppState, RouterReducerState>(
"router"
)
export const {
// 取得和目前路由相關的資訊 (路由參數、路由配置等)
selectCurrentRoute,
// 取得網址列中 # 號後面的內容 selectFragment,
// 取得路由查詢參數 selectQueryParams,
// 取得具體的某一個查詢參數 selectQueryParam('name')
selectQueryParam,
// 取得動態路由參數 selectRouteParams,
// 取得某一個具體的動態路由參數 selectRouteParam('name')
selectRouteParam,
// 取得路由自訂資料 selectRouteData,
// 取得路由的實際存取位址 selectUrl
} = getSelectors(selectRouter) // home.component.ts
import { select, Store } from "@ngrx/store"
import { AppState } from "src/app/store"
import { selectQueryParams } from "src/app/store/selectors/router.selectors"
export class AboutComponent {
constructor(private store: Store<AppState>) {
this.store.pipe(select(selectQueryParams)).subscribe(console.log)
}
}