Angular与Ngrx

Store是受 Redux 启发的用于 Angular 应用程序的 RxJS驱动的全局状态管理。Store是一个受控状态容器,旨在帮助在Angular之上编写高性能、一致的应用程序。

常规条件下构建Angular项目

  • 运行命令ng new angular-ngrx
  • 进入到新创建的项目文件夹cd angular-ngrx
  • 通过VSCode打开项目code .
  • 执行npm start检查项目初始化是否正确

Angular常规情况下共享数据

  • 对于Angular Component内或者Componenet之间,我们常规情况下通过Service来实现依赖注入并共享数据

  • 添加 ProductComponent并实现新增、删除功能

  • 通过ng g c product来创建新的组件

  • src/app目录下新建目录models,actions,reducers,states,

  • models目录下新建product.model.ts

    1
    2
    3
    4
    export interface Product{
    name:string;
    price:number;
    }
  • 修改product.component.ts实现数据加载,新增,删除

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    import { Component, OnInit } from '@angular/core';
    import { Product } from '../models/product.model';

    @Component({
    selector: 'app-product',
    templateUrl: './product.component.html',
    styleUrls: ['./product.component.css']
    })
    export class ProductComponent implements OnInit {
    products:Product[]
    newProduct:Product = {name:'',price:0}
    constructor() { }

    ngOnInit() {
    this.products = [
    {name:'Product A', price:200},
    {name:'Product B', price:300},
    {name:'Product C', price:500},
    ]
    }
    addProduct(product:Product){
    this.products.push(Object.assign({},product));
    }
    removeProduct(index:number){
    this.products.splice(index, 1);
    }
    }

  • 修改app.module.ts添加FormsModule

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { ProductComponent } from './product/product.component';
    import { FormsModule } from '@angular/forms';

    @NgModule({
    declarations: [
    AppComponent,
    ProductComponent
    ],
    imports: [
    FormsModule,
    BrowserModule,
    AppRoutingModule
    ],
    providers: [],
    bootstrap: [AppComponent]
    })
    export class AppModule { }

  • 修改product.component.html

    1
    2
    3
    4
    5
    6
    7
    8
    <input [(ngModel)]="newProduct.name"/>
    <input [(ngModel)]="newProduct.price"/>
    <button (click)="addProduct(newProduct)">新增</button>
    <ul>
    <li *ngFor="let product of products;let i = index">
    {{ product.name }} {{product.price}} <button (click)="removeProduct(i)">删除</button>
    </li>
    </ul>
  • 测试界面,可以实现简单的新增,删除,这里的新增删除都是通过products变量来管理的,如果我们想跨组件新增,删除,获取products就没有办法实现

    通过Ngrx来管理Angular项目的数据

  • Ngrx通过Store来管理并在组件间共享数据的状态

  • 我们通过StoreActionReduer来改写上面的常规共享数据

  • 执行命令npm install @ngrx/store安装ngrx

  • actions文件夹下新建product.action.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import { Action } from "@ngrx/store";
    import { Product } from "../models/product.model";

    export const enum ProductActionType {
    ProductAdd = '[Product Component] Product Add',
    ProductRemove = '[Product Component] Product Remove'
    }

    export class ProductAddAction implements Action{
    type = ProductActionType.ProductAdd;
    constructor(payload:Product){}
    }

    export class ProductRemoveAction implements Action{
    type = ProductActionType.ProductRemove;
    constructor(payload:number){}
    }

    export type ProductActions =
    ProductAddAction |
    ProductRemoveAction;
  • reducers目录下创建product.reducer.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import { ProductActions, ProductActionType } from "../actions/product.action";
    import { Product } from "../models/product.model";

    export const initialProductState:Product[] =[
    {name:'Product A', price:200},
    {name:'Product B', price:300},
    {name:'Product C', price:500}
    ]
    export function ProductReducer(state:Product[] = initialProductState, action:ProductActions){
    switch(action.type){
    case ProductActionType.ProductAdd:
    return [...state, Object.assign({},action.payload) ];
    case ProductActionType.ProductRemove:
    state.splice(action.payload,1);
    return state;
    default:
    return state;
    }
    }
  • 修改app.module.ts注册StoreModule

    1
    2
    3
    4
    5
    6
    imports: [
    FormsModule,
    BrowserModule,
    AppRoutingModule,
    StoreModule.forRoot({products:ProductReducer})
    ],
  • 更新product.component.ts以启用Store

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    import { Component, OnInit } from '@angular/core';
    import { Store } from '@ngrx/store';
    import { ProductAddAction, ProductRemoveAction } from '../actions/product.action';
    import { Product } from '../models/product.model';

    @Component({
    selector: 'app-product',
    templateUrl: './product.component.html',
    styleUrls: ['./product.component.css']
    })
    export class ProductComponent implements OnInit {
    products: Product[]
    newProduct:Product = {name:'',price:0}
    constructor(private productStore:Store<{products:Product[]}>) { }

    ngOnInit() {
    // this.products = [
    // {name:'Product A', price:200},
    // {name:'Product B', price:300},
    // {name:'Product C', price:500},
    // ]
    this.productStore.select('products').pipe().subscribe((data:Product[]) => this.products = data);
    }
    addProduct(product:Product){
    //this.products.push(Object.assign({},product));
    this.productStore.dispatch(new ProductAddAction(product));
    }
    removeProduct(index:number){
    //this.products.splice(index, 1);
    this.productStore.dispatch(new ProductRemoveAction(index));
    }
    }

    引入State来管理对象的多个属性

  • states目录下创建product.state.ts

    1
    2
    3
    4
    5
    import { Product } from "src/app/models/product.model";

    export interface ProductState{
    products:Product[]
    }
  • 修改product.reducer.ts创建createFeatureSelectorcreateSelector

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import { createFeatureSelector, createSelector } from "@ngrx/store";
    import { ProductState } from "src/state/product.state";
    import { ProductActions, ProductActionType } from "../actions/product.action";
    import { Product } from "../models/product.model";

    export const productFeatureState = createFeatureSelector<ProductState>('productState');
    export const productState = createSelector(
    productFeatureState,
    state => state.products
    )

    export const initialProductState:Product[] =[
    {name:'Product A', price:200},
    {name:'Product B', price:300},
    {name:'Product C', price:500}
    ]
    export function ProductReducer(state:Product[] = initialProductState, action:ProductActions){
    switch(action.type){
    case ProductActionType.ProductAdd:
    return [...state, Object.assign({},action.payload) ];
    case ProductActionType.ProductRemove:
    state.splice(action.payload,1);
    return state;
    default:
    return state;
    }
    }
  • 创建product-action-reducer-map.ts

    1
    2
    3
    4
    5
    6
    7
    import { ActionReducerMap } from "@ngrx/store";
    import { ProductReducer } from "src/app/reducers/product.reducer";
    import { ProductState } from "../state/product.state";

    export const ProductActionReduerMap:ActionReducerMap<ProductState> = {
    products: ProductReducer
    }
  • 修改app.module.ts注入productFeatureState

    1
    2
    StoreModule.forFeature('productState', ProductActionReduerMap),
    StoreModule.forRoot({})
  • 修改product.component.ts

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export class ProductComponent implements OnInit {
    products: Product[]
    newProduct:Product = {name:'',price:0}
    constructor(private productStore:Store<ProductState>) { }

    ngOnInit() {
    // this.products = [
    // {name:'Product A', price:200},
    // {name:'Product B', price:300},
    // {name:'Product C', price:500},
    // ]
    //this.productStore.select('products').pipe().subscribe((data:Product[]) => this.products = data);
    this.productStore.select(productState).pipe().subscribe((data:Product[]) => this.products = data);
    }
    addProduct(product:Product){
    //this.products.push(Object.assign({},product));
    this.productStore.dispatch(new ProductAddAction(product));
    }
    removeProduct(index:number){
    //this.products.splice(index, 1);
    this.productStore.dispatch(new ProductRemoveAction(index));
    }
    }

    FAQ

  • 如果遇到错误Cannot delete property '2' of [object Array]

    • 通过命令npm install @ngrx/store@8.5.2安装"@ngrx/store": "^8.5.2"
  • 如果遇到错误ERROR TypeError: Cannot assign to read only property '1' of object '[object Array]'

    • 通过命令npm install @ngrx/store@8.5.2安装"@ngrx/store": "^8.5.2"

      发布

  • 知乎

    引用