У меня есть угловой шаблон покупок, и я пытаюсь подключить службу корзины, которая использовалась к моему бэкэнду, поэтому я сделал это, но чтобы упростить работу с базой данных, мне пришлось изменить запрос к моему бэкэнду с {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;
}
Что-то, чего я не понимаю: почему вы обрабатываете элементы из p во вложенном вызове map как CarItem? Разве тип s не должен быть CartResponse?
Кроме того, пожалуйста, добавьте определения используемых интерфейсов, особенно CartResponse.
@Jota.Toledo Я добавил определения интерфейсов к вопросу.
Добавил ответ :)





Это немного сложно, но вы можете использовать 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, и я не могу скомпилировать этот код, вы можете помочь?
Хорошо, я поместил демо вместе в конце моего поста. Также я исправил кое-что в своем коде, поэтому обновите свой, если вы его только что скопировали и вставили. И еще: самая сложная часть изучения Angular — это реактивное программирование (rxjs). Так что сохраняйте хладнокровие и не сдавайтесь. И обратите внимание на мой оператор импорта в начале, потому что я использую псевдоним оператора of для observableOf
Этот сайт может быть полезен в процессе освоения реактивного программирования: Learnrxjs.io, но, к сожалению, он не полностью обновлен (например, forkJoin еще не используют последнюю версию)
не должен ли тип элемента быть CartResponse вместо CartItem, потому что cartResponse тот, у кого есть productId, поэтому я могу передать его this.productService.getProduct(item.product)); ?
Да, ты был прав. Я увидел новую информацию, которую вы добавили, и исправил мой код.
эта строка продолжает выдавать ошибку .reduce((acc, a) => { acc[a.id] = a; return acc; }, {}); TypeError: Невозможно прочитать свойство 'id' неопределенного
Я почти уверен, что вы могли бы сэкономить на создании products; при создании itemsToReturn вы можете добавить значение показатель к вызову map и получить доступ resultOfForkJoin с этим индексом.
@Jota.Toledo, я не уверен, следую ли я тому, что вы предлагаете
Комментарий был адресован @jpavel
Я изменил эту строку .reduce((acc, a) => { acc[a.id] = a; return acc; }, {}); to .reduce((acc, a) => { acc[a] = a; return acc; }, {}); теперь он возвращает результат, но без продукта!
Что возвращает this.productService.getProduct(item.product))? Я предполагаю, что это всегда продукт (никогда null, или undefined, или array)
Вы можете поставить console.info(resultOfForkJoin), чтобы проверить, что передается map.
Вы можете пойти со следующим подходом, обратите внимание, что я создал несколько фабричные методы с поддельными данными, чтобы проверить их. Вы можете заменить их своими реальными реализациями:
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);
Вы можете увидеть код, работающий в этот блиц
Умно и элегантно!
Полный раздел
mapв методеgetAllProductsне обязателен. Убери это.