ngrx概念
- 一個框架
- 用於構建Angular8響應式應用
- 用於狀態管理
- 使用可觀察對象
- 使用Typescript
- 使用OnPush策略,變動檢測更高效
- 狀態序列化存儲
- 易於測試
原理圖
- component觸發特定action(事件)
- store中存儲着state、reducer。state對象存放着感興趣的字段
- action能夠觸發effect獲取業務數據,effect處理完畢,再次觸發action進行通知
- action也能夠直接觸發reducer更新state
- reducer產生新的state
- selector層層篩選出state中感興趣的那部分字段
- state 感興趣的字段修改了,component對應的視圖進行響應式變化
- 綜上完成解耦,一個輪迴完成視圖狀態修改
什麼時候使用ngrx
組件之間共享的state管理
解耦組件與service之間耦合
簡單入門
初始化項目
ng new ngrx-demo
複製代碼
> ng new ngrx-demo
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS [ https://sass-lang.com/
documentation/syntax
CREATE ngrx-demo/README.md (1026 bytes)
CREATE ngrx-demo/.editorconfig (246 bytes)
CREATE ngrx-demo/.gitignore (631 bytes)
CREATE ngrx-demo/angular.json (3705 bytes)
CREATE ngrx-demo/package.json (1295 bytes)
CREATE ngrx-demo/tsconfig.json (543 bytes)
CREATE ngrx-demo/tslint.json (1953 bytes)
CREATE ngrx-demo/browserslist (429 bytes)
CREATE ngrx-demo/karma.conf.js (1021 bytes)
CREATE ngrx-demo/tsconfig.app.json (270 bytes)
CREATE ngrx-demo/tsconfig.spec.json (270 bytes)
CREATE ngrx-demo/src/favicon.ico (948 bytes)
CREATE ngrx-demo/src/index.html (294 bytes)
CREATE ngrx-demo/src/main.ts (372 bytes)
....
added 1461 packages from 1071 contributors in 85.831s
Successfully initialized git.
複製代碼
安裝ngrx
npm install @ngrx/store --save
複製代碼
建立Angular組件my-counter
ng generate @schematics/angular:component my-counter --style=scss <
複製代碼
定義action
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');
複製代碼
定義reducer初始狀態爲數值
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export const initialState = 0;
const _counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => 0),
);
export function counterReducer(state, action) {
return _counterReducer(state, action);
}
複製代碼
註冊state
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
StoreModule.forRoot({ count: counterReducer }) // 註冊時指定key
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
複製代碼
定義組件
複製代碼
<button id="increment" (click)="increment()">Increment</button>
<div>Current Count: {{ count$ | async }}</div>
<button id="decrement" (click)="decrement()">Decrement</button>
<button id="reset" (click)="reset()">Reset Counter</button>
複製代碼
組件觸發action
複製代碼
import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from '../counter.actions';
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
styleUrls: ['./my-counter.component.css'],
})
export class MyCounterComponent {
count$: Observable<number>;
constructor(private store: Store<{ count: number }>) { //這裏須要key-count保持一致,
this.count$ = store.pipe(select('count')); //這裏關鍵點,在於app.module.ts註冊時候的key
}
increment() {
console.log('counter increment...');
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
複製代碼
組件生效
<div>
this is app component
<app-my-counter></app-my-counter>
</div>
複製代碼
store.pipe寫法解惑
export declare class Store<T> extends Observable<T> implements Observer<Action> {
....
}
複製代碼
store.select也是Observable
export declare function select<T, Props, K>(mapFn: (state: T, props: Props) => K, props?: Props): (source$: Observable<T>) => Observable<K>;
複製代碼
this.store
.select(fromStore.getProductsState)
.map(state => state.pizzas)
.map(pizzas => pizza.entities);
複製代碼
進階
當reducer初始狀態state爲對象時
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export interface State {
away: number;
}
export const initialState: State = {
away: 0,
};
const _counterReducer = createReducer(initialState,
on(increment, (state) => {
console.log('reducer increment....');
return {...state, away: state.away + 1};
}),
on(decrement, state => ({...state, away: state.away - 1})),
on(reset, state => ({...state, away: 0})),
);
export function counterReducer(state, action) {
return _counterReducer(state, action);
}
複製代碼
延遲加載state註冊
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
imports: [
StoreModule.forFeature('countFeture', counterReducer)
],
})
export class ScoreboardModule {}
複製代碼
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
import { ScoreboardModule } from './feature.module';
@NgModule({
declarations: [
AppComponent,
MyCounterComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}),
ScoreboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
複製代碼
root中配置前置reducer
import { ActionReducer, MetaReducer } from '@ngrx/store';
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function( state, action) {
console.log('pre state', state);
console.log('pre action', action);
return reducer(state, action);
};
}
export const metaReducers: MetaReducer<any>[] = [debug];
複製代碼
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
import { ScoreboardModule } from './feature.module';
import { metaReducers } from './pre.reducer';
@NgModule({
declarations: [
AppComponent,
MyCounterComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}, { metaReducers }), //前置reducer
ScoreboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
複製代碼
counter increment...
pre state {countFeture: {…}}
pre action {type: "[Counter Component] Increment"}
reducer increment....
複製代碼
feature中配置前置reducer
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { metaReducers } from './pre.reducer';
@NgModule({
imports: [
StoreModule.forFeature('countFeture', counterReducer , { metaReducers })
],
})
export class ScoreboardModule {}
複製代碼
counter increment...
pre state {countFeture: {…}}
pre action {type: "[Counter Component] Increment"}
reducer increment....
複製代碼
selector解讀
Selector 源碼 -- state相關的函數
export declare type Selector<T, V> = (state: T) => V;
複製代碼
SelectorWithProps源碼
export declare type SelectorWithProps<State, Props, Result> = (state: State, props: Props) => Result;
複製代碼
MemoizedSelector是Selector子集
export interface MemoizedSelector<State, Result, ProjectorFn = DefaultProjectorFn<Result>> extends Selector<State, Result> {
release(): void;
projector: ProjectorFn;
setResult: (result?: Result) => void;
}
export interface MemoizedSelectorWithProps<State, Props, Result, ProjectorFn = DefaultProjectorFn<Result>> extends SelectorWithProps<State, Props, Result> {
release(): void;
projector: ProjectorFn;
setResult: (result?: Result) => void;
}
複製代碼
createFeatureSelector源碼
export declare function createFeatureSelector<T>(featureName: string): MemoizedSelector<object, T>;
export declare function createFeatureSelector<T, V>(featureName: keyof T): MemoizedSelector<T, V>;
複製代碼
createSelector源碼
export declare function createSelector<State, S1, Result>(s1: Selector<State, S1>, projector: (s1: S1) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, Result>(s1: SelectorWithProps<State, Props, S1>, projector: (s1: S1, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, Result>(selectors: [Selector<State, S1>], projector: (s1: S1) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, Result>(selectors: [SelectorWithProps<State, Props, S1>], projector: (s1: S1, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, projector: (s1: S1, s2: S2) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, projector: (s1: S1, s2: S2, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, Result>(selectors: [Selector<State, S1>, Selector<State, S2>], projector: (s1: S1, s2: S2) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>], projector: (s1: S1, s2: S2, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, projector: (s1: S1, s2: S2, s3: S3) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>], projector: (s1: S1, s2: S2, s3: S3) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>], projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>], projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, s6: Selector<State, S6>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>, Selector<State, S6>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, s6: Selector<State, S6>, s7: Selector<State, S7>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, s7: SelectorWithProps<State, Props, S7>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>, Selector<State, S6>, Selector<State, S7>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6>, SelectorWithProps<State, Props, S7>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, s6: Selector<State, S6>, s7: Selector<State, S7>, s8: Selector<State, S8>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, S8, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, s7: SelectorWithProps<State, Props, S7>, s8: SelectorWithProps<State, Props, S8>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>, Selector<State, S6>, Selector<State, S7>, Selector<State, S8>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, S8, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6>, SelectorWithProps<State, Props, S7>, SelectorWithProps<State, Props, S8>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
e
複製代碼
select源碼
export declare function select<T, Props, K>(mapFn: (state: T, props: Props) => K, props?: Props): (source$: Observable<T>) => Observable<K>;
//其中mapFn: (state: T, props: Props) => K 就是SelectorWithProps
複製代碼
總結
- createSelector(): 入參state函數,返回selector純函數;形式爲state函數;能夠嵌套
- createFeatureSelector:入參state函數,返回selector純函數;形式爲state函數;能夠嵌套
- selector():入參state函數,也可入參selector純函數;返回Observable可觀察對象
selector實踐
定義state對象
export interface State {
count: number;
}
export interface AppState {
feature: State;
}
複製代碼
feature中註冊
@NgModule({
imports: [
StoreModule.forFeature('feature', counterReducer )
],
})
export class ScoreboardModule {}
複製代碼
定義關心的state字段函數
export const countFeatureFn = (state: AppState, props) => {
console.log('feature....', state);
return state.feature;
};
export const countStateFn = (state: State, props) => {
console.log('state....', state);
return state.count * props.multiply;
};
複製代碼
建立selector函數
export const countStateSelector = createSelector(countFeatureFn, countStateFn);
複製代碼
經過select(),創建與store的關聯,訂閱數據
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
styleUrls: ['./my-counter.component.scss'],
})
export class MyCounterComponent {
count$: Observable<number>;
constructor(private store: Store<{feature: State }>) {
this.count$ = store.pipe(select(countStateSelector, { multiply: 3}));
}
....
}
複製代碼
觸發store中state改變,刷新component便可
increment() {
// console.log('counter increment...');
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
複製代碼
引入常量,優化
export const featureKey = 'feature';
複製代碼
使用createFeatureSelector
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { featureKey } from './feature.enum';
export interface State {
count: number;
}
export interface AppState {
[featureKey]: State;
}
// export const countFeatureFn = (state: AppState, props) => {
// console.log('feature....', state);
// return state[featureKey];
// };
export const countStateFn = (state: State, props) => {
console.log('state....', state);
return state.count * props.multiply;
};
export const selectFeature = createFeatureSelector<AppState, State>(featureKey);
export const countStateSelector = createSelector(selectFeature, countStateFn);
複製代碼
effects 實踐
effects用途
- 監聽特定動做,調用業務邏輯。譬如登陸邏輯。
- 特定邏輯調用完畢後,發射特定的動做,觸發reducer,修改狀態,更新組件。
添加effects
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { EMPTY } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { HelloService } from './hello.service';
import { increment, decrement, reset, hello, helloSuccess } from './counter.actions';
@Injectable()
export class HelloEffects {
loadMovies$ = createEffect(() => this.actions$.pipe(
ofType(hello),
mergeMap((action) => this.helloService.getHello(action)
.pipe(
map((movies: string) => {
console.log('in effect map...', movies);
return helloSuccess({title: movies});
}),
catchError(() => EMPTY)
)
)
)
);
constructor(
private actions$: Actions,
private helloService: HelloService
) {}
}
複製代碼
root中註冊effects
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
import { ScoreboardModule } from './feature.module';
import { metaReducers } from './pre.reducer';
import { EffectsModule } from '@ngrx/effects';
import { HelloEffects } from './hello.effects';
@NgModule({
declarations: [
AppComponent,
MyCounterComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}),
EffectsModule.forRoot([HelloEffects]),
ScoreboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
複製代碼
添加service
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HelloService {
constructor() { }
getHello() {
// console.log('service get hello....');
return of('hello world ngrx');
}
}
複製代碼
action修改
import { createAction, props } from '@ngrx/store';
import { State, TitleState } from './counter.selector';
export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');
export const hello = createAction('[Hello Service] call', props<TitleState>());
export const helloSuccess = createAction('[Hello Service] call success', props<TitleState>());
複製代碼
組件中觸發effect定義的動做
import { Component, OnInit } from '@angular/core';
import { Store, select, createSelector, createFeatureSelector } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from '../counter.actions';
import { countStateSelector, titleStateSelector, State, AppState } from '../counter.selector';
import { HelloService } from '../hello.service';
import { hello } from '../counter.actions';
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
styleUrls: ['./my-counter.component.scss'],
})
export class MyCounterComponent implements OnInit{
count$: Observable<number>;
title$: Observable<string>;
constructor(private store: Store<AppState>) {
this.count$ = store.pipe(select(countStateSelector, { multiply: 2}));
this.title$ = store.pipe(select(titleStateSelector));
}
ngOnInit(): void {
this.store.dispatch(hello({title: 'hello dispatch', name: 'ngrx '}));
}
increment() {
// console.log('counter increment...');
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
複製代碼
selectors篩選特定數據
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { featureKey } from './feature.enum';
export interface AppState {
[featureKey]: State;
}
export interface State {
count: number;
}
export interface TitleState {
title: string;
name?: string;
}
export const countStateFn = (state: State, props) => {
console.log('count state....', state);
return state.count * props.multiply;
};
export const titleStateFn = (state: TitleState) => {
console.log('title state....', state);
return state.title;
};
export const countFeature = createFeatureSelector<AppState, State>(featureKey);
export const titleFeature = createFeatureSelector<AppState, TitleState>(featureKey);
// logic selector
export const countStateSelector = createSelector(countFeature, countStateFn);
export const titleStateSelector = createSelector(titleFeature, titleStateFn);
複製代碼
reducer進行數據處理
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset, helloSuccess } from './counter.actions';
import { State, TitleState } from './counter.selector';
export const initialState: State & TitleState = {
count: 0,
title: '',
};
const localCounterReducer = createReducer(initialState,
on(increment, (state) => {
console.log('incre....', state);
const res = {...state, count: state.count + 1};
return res;
}),
on(decrement, state => ({...state, count: state.count - 1})),
on(reset, state => ({...state, count: 0})),
on(helloSuccess, (state, { title } ) => {
console.log('param title ....', title);
return { ...state, title } ;
})
);
export function counterReducer(state, action) {
// console.log('counter reducer...', state, action);
return localCounterReducer(state, action);
};
複製代碼
參考文獻: