【问题标题】:Mapping http response to interface in angular 7将http响应映射到角度7中的接口
【发布时间】:2019-07-12 22:46:22
【问题描述】:

我有角度购物模板,我正在尝试将用于我的后端的购物车服务连接起来,所以我已经这样做了,但是为了使数据库中的事情更容易,我不得不将请求从 {quantity, amount, product} 更改为我的后端到{quantity, amount, productId},所以我不必将整个产品存储在数据库中并且到目前为止它工作正常,现在当我得到响应时,我想将它映射到它的界面,但首先我需要找到带有 id 的产品如果我有整个产品,我可以在响应中映射响应,但我又不想将产品保存在我的数据库中。这是我的映射代码,但我在调用按 id 查找产品的函数时遇到问题:

this.getAllProducts().pipe(map(p => p.map((s): CartItem => ({
  currency: s.currency,
  product: this.productService.getProduct(s.product).subscribe(p => p),
  quantity: s.quantity,
  selectedColor: s.selectedColor,
  selectedSize: s.selectedSize,
  userId: s.userId
}))));


  private getAllProducts(): Observable<CartResponse[]> {
    return this.http.get<CartResponse[]>('http://localhost:8082/cart/getAllItems?userId=111').pipe(
      map(o => o.map((sp): CartResponse => ({
        quantity: sp.quantity,
        currency: sp.currency,
        product: sp.product,
        selectedColor: sp.selectedColor,
        selectedSize: sp.selectedSize,
        userId: sp.userId
      }))));
  }


export interface CartResponse {
    product: string;
    quantity: number;
    selectedSize: any;
    selectedColor: string;
    currency: string;
    userId: string;
}

export interface CartItem {
    product: Product;
    quantity: number;
    selectedSize: any;
    selectedColor: string;
    currency: string;
    userId: string;
}

【问题讨论】:

  • 不需要getAllProducts 方法中的完整map 部分。删除它。
  • 我不明白的事情:为什么在嵌套的map 调用中处理来自p 的元素为CarItemCartResponse 不应该是s 的类型吗?
  • 另外,请添加所用接口的定义,特别是CartResponse
  • @Jota.Toledo 我已将接口定义添加到问题中
  • 添加了答案:)

标签: angular http rxjs angular-httpclient


【解决方案1】:

您可以使用以下方法,请注意我创建了一些带有假数据的 工厂方法 以便对其进行测试。您可以将它们替换为您的实际实现:

import { of, Observable } from 'rxjs';
import { map, mergeMap, toArray } from 'rxjs/operators';

export interface Product {
  id: string;
  name: string;
}

export interface CartResponse {
  product: string;
  quantity: number;
  selectedSize: any;
  selectedColor: string;
  currency: string;
  userId: string;
}

export interface CartItem {
  product: Product;
  quantity: number;
  selectedSize: any;
  selectedColor: string;
  currency: string;
  userId: string;
}

const fakeGetAllProducts = (): Observable<CartResponse[]> => of<CartResponse[]>([
  { currency: "US", product: "PX1", quantity: 10, selectedColor: "red", selectedSize: "L", userId: "UX1" },
  { currency: "EU", product: "PX50", quantity: 10, selectedColor: "blue", selectedSize: "S", userId: "UX2" }
]);
const fakeGetProduct = (id: string): Observable<Product> => of<Product>({ id, name: `Product ${id}` });

// Here the cart response is destructured into 2 values: the product id and everything else
const mapResponseToItem = ({ product, ...noProduct }: CartResponse): Observable<CartItem> => fakeGetProduct(product).pipe(
  map<Product, CartItem>(product => ({ ...noProduct, product }))
);

fakeGetAllProducts().pipe(
  // flatten the array in order to process single items from it sequentially
  mergeMap(items => items),
  // map a cart response into a cart item observable and flatten it
  mergeMap(mapResponseToItem),
  // collect the sequentially processed elements into an array
  toArray()
).subscribe(console.log);

你可以看到代码在this blitz中运行

【讨论】:

  • 聪明优雅!
【解决方案2】:

这有点棘手,但您可以使用 switchMap + forkJoin 来完成这项工作。注意我使用的是rxjs@6.5.1。这很重要,因为在以前的版本中 forkJoin 不接收数组作为参数。

import {forkJoin, of as observableOf} from 'rxjs';
import {switchMap, map} from 'rxjs/operators';

....

this.getAllProducts().pipe(
  switchMap((items: CartResponse[]) => {
    // build an array of observables to get all products
    const arrayOfObservables = items.map((item: CartResponse) => 
        this.productService.getProduct(item.product));

    // now go to the database, grab the products, and combine the response
    // with the array of CartResponse you already had
    return forkJoin([
      observableOf(items), 
      ...arrayOfObservables
    ]);
  }),
  map((resultOfForkJoin) => {
    // Notice that resultOfForkJoin is an array
    // - The first item of the array is the original items 
    //    returned by getAllProducts(): CartResponse[]
    // - The rest of the elements of the array are the products

    const items: CartResponse[] = resultOfForkJoin[0];

    // filter the products of resultOfForkJoin and
    // build a JSON of them (for performance), where the attributes 
    // are the products id (I'm suposing each product
    // has a property named after 'id')
    const products = resultOfForkJoin
        .filter((r,index) => index > 0)
        .reduce((acc,a) => {acc[a.id] = a; return acc;}, {});

    // now you can assemble the desired items
    const itemsToReturn = items.map((s: CartResponse): CartItem => ({
      currency: s.currency,
      product: products[s.product],
      quantity: s.quantity,
      selectedColor: s.selectedColor,
      selectedSize: s.selectedSize,
      userId: s.userId
    }));

    return itemsToReturn;
  })
).subscribe((item) => console.log(item));

更新:如果您使用的是以前版本的 rxjs,您可以将 forkJoin 切换为:

forkJoin(observableOf(items), ...arrayOfObservables);

stackblitz demo.

【讨论】:

  • 抱歉,我是 Angular 新手,无法编译此代码,您能帮忙吗?
  • 好的,我在帖子末尾放了一个演示。我还在我的代码中修复了一个东西,所以如果你刚刚复制并粘贴了它,请更新你的。还有一件事:学习 Angular 最难的部分是响应式编程(rxjs)。所以,保持冷静,不要放弃。请注意我在开头的导入语句,因为我将 of 运算符别名为 observableOf
  • 这个站点在掌握响应式编程的过程中很有用:learnrxjs.io,但遗憾的是它还没有完全更新(例如forkJoin,暂时不要使用最新版本)
  • 不应该商品类型应该是 CartResponse 而不是 CartItem 因为 cartResponse 是有 productId 的,所以我可以将它传递给 this.productService.getProduct(item.product)); ?
  • 是的,你是对的。我看到了您添加的新信息并修复了我的代码。
猜你喜欢
  • 2018-10-31
  • 2019-06-20
  • 2018-03-07
  • 2011-01-10
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
  • 2021-12-25
  • 2020-11-23
相关资源
最近更新 更多