I'm using WooCommerce v3 api, my goal is to show a list with all the categories and horizontally show all the products by category, like:
Category name:
product product1 product2
Category 2 name:
product product1 product2
but there is not endpoint to get all this, so I get all the categories first with
getAllCats(): Observable<any>{
const req = this.httpS.get(`${API.prod.url+API.prod.woo+'products/categories/?consumer_key='+API.prod.consumer_key+'&consumer_secret='+API.prod.consumer_secret}`);
return req;
}
getProductsByCat(catId: number): Observable<any>{
const req = this.httpS.get(`${API.prod.url+API.prod.woo+'products?consumer_key='+API.prod.consumer_key+'&consumer_secret='+API.prod.consumer_secret+'&category='+catId}`);
return req;
}
and then on my component I am doing this
categories: any = {};
cats: Array<any> = [];
products: Array<any> = [];
catsWithAllProducts: any = {
category: [{
products: []
}]
};
kats: Category[] = [];
showAllCatsWithProducts(){
this.pService.getAllCats().subscribe((cats) => {
for (const [i,cat] of cats.entries()) {
this.pService.getProductsByCat(cat.id).subscribe((products) => {
this.cats.push(cat);
this.products.push(products);
this.catsWithAllProducts.category = cat;
this.kats.push(this.catsWithAllProducts); //new categories array with products
});
}
});
}
I have two problems, the products[] is empty, I'm confused with, how to achieve to create a new object or array or array of objects with all the products inside a category. Right what is printed on screen is all the categories but with repeated name:
I have 10 categories, and it shows the first name that receives * the number of categories. What I've been thinking is that first it should be async, I mean is an observable but it is pushing to the array each element with or without response yet. I'm kind of stuck here, thanks for reading me.
I'm not really sure, but try if this changes over your code solve your possible async problem:
getAllCats(): Observable<any>{
return this.httpS.get(`${API.prod.url+API.prod.woo+'products/categories/?consumer_key='+API.prod.consumer_key+'&consumer_secret='+API.prod.consumer_secret}`);
}
getProductsByCat(catId: number): Observable<any>{
return this.httpS.get(`${API.prod.url+API.prod.woo+'products?consumer_key='+API.prod.consumer_key+'&consumer_secret='+API.prod.consumer_secret+'&category='+catId}`);
}
showAllCatsWithProducts() {
this.pService.getAllCats()
.pipe(
switchMap( cat => this.pService.getProductsByCat(cat.id) ),
)
.subscribe( products => {
this.cats.push(cat);
this.products.push(products);
this.catsWithAllProducts.category = cat;
this.kats.push(this.catsWithAllProducts);
});
)
I'd recommend first changing your service to define what you need as observables, hiding as much complexity from the components as possible, like this:
export interface Category {
id: number;
}
export interface Product {
id: number;
}
@Injectible()
export class pService {
readonly categories$: Observable<Category[]> = this.httpS.get(`${API.prod.url+API.prod.woo+'products/categories/?consumer_key='+API.prod.consumer_key+'&consumer_secret='+API.prod.consumer_secret}`).pipe(
take(1),
shareReplay()
);
readonly productsPerCategory$: Observable<[Category, Product[]]> = this.categories$.pipe(
mergeMap(category => this.getProductsByCat(category.id).pipe(
map(products => [category, products])
)),
shareReplay()
);
readonly allProducts$: Observable<Product[]> = this.productsPerCategory$.pipe(
mergeMap(ppc => from(ppc[1])),
distinct((p: Product) => p.id), // if products overlap, otherwise remove this line if every product is in only one category.
shareReplay()
);
readonly allProducts: Promise<Product []> = this.allProducts$.pipe(toArray()).toPromise;
private getProductsByCat(catId: number): Observable<Product[]>{
return this.httpS.get(`${API.prod.url+API.prod.woo+'products?consumer_key='+API.prod.consumer_key+'&consumer_secret='+API.prod.consumer_secret+'&category='+catId}`);
}
}
This is all without a subscribe. You might be able to get away without a single subscribe in your comment, too, if you use the async pipe, depending on what you need.
I handle it with async/await and changing the regular for to forEach at showAllCatsWithProducts() method
async showAllCatsWithProducts(){
this.pService.getAllCats().subscribe((cats) => {
cats.forEach(async cat => { //for each category
const response = await this.pService.getProductsByCat(cat.id).subscribe(res => { //It's gonna wait por their products
this.categories.push({id: cat.id,name: cat.name,slug:cat.slug, products: res}); //Push category with products
});
});
});
}