Отображение http-ответа на интерфейс в angular 7

У меня есть угловой шаблон покупок, и я пытаюсь подключить службу корзины, которая использовалась к моему бэкэнду, поэтому я сделал это, но чтобы упростить работу с базой данных, мне пришлось изменить запрос к моему бэкэнду с {quantity, amount, product} на {quantity, amount, productId}, поэтому я не мне не нужно хранить весь продукт в базе данных, и пока он работает нормально, теперь, когда я получил ответ, я хочу сопоставить его с его интерфейсом, но сначала мне нужно найти продукт с идентификатором, который у меня есть в ответ, Я могу сопоставить ответ, если у меня есть весь продукт, но опять же я не хочу, чтобы продукт сохранялся в моей базе данных. вот мой код для отображения, но у меня есть проблема с вызовом моей функции, которая находит продукт по идентификатору:

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;
}

Полный раздел map в методе getAllProducts не обязателен. Убери это.

Jota.Toledo 13.07.2019 14:06

Что-то, чего я не понимаю: почему вы обрабатываете элементы из p во вложенном вызове map как CarItem? Разве тип s не должен быть CartResponse?

Jota.Toledo 13.07.2019 14:07

Кроме того, пожалуйста, добавьте определения используемых интерфейсов, особенно CartResponse.

Jota.Toledo 13.07.2019 14:08

@Jota.Toledo Я добавил определения интерфейсов к вопросу.

Khaled Jamal 13.07.2019 14:13

Добавил ответ :)

Jota.Toledo 13.07.2019 14:37
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
1
5
511
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это немного сложно, но вы можете использовать switchMap + forkJoin, чтобы выполнить эту работу. Обратите внимание, что я использую [email protected]. Это важно, потому что в предыдущих версиях 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.info(item));

ОБНОВЛЕНИЕ: если вы используете предыдущие версии rxjs, вы можете переключить forkJoin на:

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

А демо стекблиц.

извините, приятель, я новичок в angular, и я не могу скомпилировать этот код, вы можете помочь?

Khaled Jamal 13.07.2019 13:27

Хорошо, я поместил демо вместе в конце моего поста. Также я исправил кое-что в своем коде, поэтому обновите свой, если вы его только что скопировали и вставили. И еще: самая сложная часть изучения Angular — это реактивное программирование (rxjs). Так что сохраняйте хладнокровие и не сдавайтесь. И обратите внимание на мой оператор импорта в начале, потому что я использую псевдоним оператора of для observableOf

julianobrasil 13.07.2019 13:53

Этот сайт может быть полезен в процессе освоения реактивного программирования: Learnrxjs.io, но, к сожалению, он не полностью обновлен (например, forkJoin еще не используют последнюю версию)

julianobrasil 13.07.2019 14:10

не должен ли тип элемента быть CartResponse вместо CartItem, потому что cartResponse тот, у кого есть productId, поэтому я могу передать его this.productService.getProduct(item.product)); ?

Khaled Jamal 13.07.2019 14:11

Да, ты был прав. Я увидел новую информацию, которую вы добавили, и исправил мой код.

julianobrasil 13.07.2019 14:16

эта строка продолжает выдавать ошибку .reduce((acc, a) => { acc[a.id] = a; return acc; }, {}); TypeError: Невозможно прочитать свойство 'id' неопределенного

Khaled Jamal 13.07.2019 14:29

Я почти уверен, что вы могли бы сэкономить на создании products; при создании itemsToReturn вы можете добавить значение показатель к вызову map и получить доступ resultOfForkJoin с этим индексом.

Jota.Toledo 13.07.2019 14:37

@Jota.Toledo, я не уверен, следую ли я тому, что вы предлагаете

Khaled Jamal 13.07.2019 14:40

Комментарий был адресован @jpavel

Jota.Toledo 13.07.2019 14:46

Я изменил эту строку .reduce((acc, a) => { acc[a.id] = a; return acc; }, {}); to .reduce((acc, a) => { acc[a] = a; return acc; }, {}); теперь он возвращает результат, но без продукта!

Khaled Jamal 13.07.2019 14:51

Что возвращает this.productService.getProduct(item.product))? Я предполагаю, что это всегда продукт (никогда null, или undefined, или array)

julianobrasil 13.07.2019 15:28

Вы можете поставить console.info(resultOfForkJoin), чтобы проверить, что передается map.

julianobrasil 13.07.2019 15:31
Ответ принят как подходящий

Вы можете пойти со следующим подходом, обратите внимание, что я создал несколько фабричные методы с поддельными данными, чтобы проверить их. Вы можете заменить их своими реальными реализациями:

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.info);

Вы можете увидеть код, работающий в этот блиц

Умно и элегантно!

julianobrasil 13.07.2019 16:08

Другие вопросы по теме