You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从API数据中提取products数组并结合NgRx在前端展示?

解决方案:提取API返回的products数组(NgRx场景)

根据你的需求,下面提供三种不同层级的处理方案,你可以根据实际业务场景选择:

方案1:在Effects中提前提取(推荐)

如果你的状态只需要products数组,不需要保留完整的API响应,直接在Effect里处理最合理,能简化后续Reducer和组件的逻辑。

修改Effect代码

在API请求的map操作中直接提取data.products:

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { of } from 'rxjs';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { ProductService } from './product.service';
import * as ProductActions from './product.actions';

@Injectable()
export class ProductEffects {
  loadProducts$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ProductActions.loadProducts),
      mergeMap(() =>
        this.productService.getProducts().pipe(
          // 直接提取data中的products数组
          map(apiRes => ProductActions.loadProductsSuccess({ products: apiRes.data.products })),
          catchError(err => of(ProductActions.loadProductsFailure({ error: err })))
        )
      )
    )
  );

  constructor(private actions$: Actions, private productService: ProductService) {}
}

对应Action定义

调整成功Action的参数,直接接收products数组:

import { createAction, props } from '@ngrx/store';
import { IProduct } from './product.model';

export const loadProducts = createAction('[Product] Load Products');
export const loadProductsSuccess = createAction(
  '[Product] Load Products Success',
  props<{ products: IProduct[] }>()
);
export const loadProductsFailure = createAction(
  '[Product] Load Products Failure',
  props<{ error: any }>()
);

Reducer代码

Reducer直接将products存入状态:

import { createReducer, on } from '@ngrx/store';
import * as ProductActions from './product.actions';
import { IProduct } from './product.model';

export interface ProductState {
  products: IProduct[];
  loading: boolean;
  error: any;
}

const initialState: ProductState = {
  products: [],
  loading: false,
  error: null
};

export const productReducer = createReducer(
  initialState,
  on(ProductActions.loadProducts, state => ({ ...state, loading: true, error: null })),
  on(ProductActions.loadProductsSuccess, (state, { products }) => ({
    ...state,
    loading: false,
    products: products
  })),
  on(ProductActions.loadProductsFailure, (state, { error }) => ({
    ...state,
    loading: false,
    error: error
  }))
);

组件中使用

直接从Store中选择products数组即可循环展示:

import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { IProduct } from './product.model';
import * as fromProduct from './product.reducer';
import * as ProductActions from './product.actions';

@Component({
  selector: 'app-product-list',
  template: `
    <div *ngFor="let product of products$ | async">
      <h3>{{ product.name }}</h3>
      <p>价格:{{ product.price }}</p>
    </div>
  `
})
export class ProductListComponent implements OnInit {
  products$: Observable<IProduct[]>;

  constructor(private store: Store<fromProduct.ProductState>) {
    this.products$ = this.store.select(fromProduct.selectProducts);
  }

  ngOnInit(): void {
    this.store.dispatch(ProductActions.loadProducts());
  }
}

(注:需要在reducer中定义selectProducts选择器:export const selectProducts = (state: ProductState) => state.products;)


方案2:在Reducer中提取

如果需要保留完整的API响应(比如要用到status等其他字段),可以在Reducer中提取products数组。

Effect代码(返回完整响应)

// Effect中返回整个API响应
map(apiRes => ProductActions.loadProductsSuccess({ response: apiRes }))

Action定义

export const loadProductsSuccess = createAction(
  '[Product] Load Products Success',
  props<{ response: any }>() // 建议定义ApiResponse接口替代any
);

Reducer代码(提取products)

export interface ProductState {
  products: IProduct[];
  rawApiResponse: any; // 存储完整API响应
  loading: boolean;
  error: any;
}

const initialState: ProductState = {
  products: [],
  rawApiResponse: null,
  loading: false,
  error: null
};

export const productReducer = createReducer(
  initialState,
  on(ProductActions.loadProducts, state => ({ ...state, loading: true, error: null })),
  on(ProductActions.loadProductsSuccess, (state, { response }) => ({
    ...state,
    loading: false,
    rawApiResponse: response,
    products: response.data.products // 在这里提取products数组
  })),
  on(ProductActions.loadProductsFailure, (state, { error }) => ({
    ...state,
    loading: false,
    error: error
  }))
);

组件中依然可以直接使用selectProducts选择器获取数组。


方案3:在组件中提取(不推荐)

如果只是临时需求,也可以在组件的store.select后通过map操作提取,但不建议这么做——组件应该只负责展示,数据转换逻辑尽量放在NgRx层,保持组件简洁。

示例代码:

// 组件中
import { map } from 'rxjs/operators';

// 假设reducer中存储了完整的API响应,并有对应的选择器
this.products$ = this.store.select(fromProduct.selectRawApiResponse).pipe(
  map(res => res?.data?.products || []) // 处理空值情况
);

总结

  • 优先选方案1:逻辑更清晰,状态只存储必要数据,减少冗余。
  • 需要保留完整API响应时选方案2:兼顾数据提取和原始响应存储。
  • 方案3尽量避免:会让组件承担额外的数据处理职责,不符合NgRx的设计理念。

内容的提问来源于stack exchange,提问作者Youzef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:40:57