0

I have an empty array(productsWithCategory) and i try to fill it on click event. But it is not working on first click. When i click once, array does not fill. After first click, everything works. But i really don't know why. Can you help me?

Sorry for my english.

category.component.html :

<h4 class="page-header">Categories</h4>
<div class="row">
  <div class="col-lg-3">
    <div class="list-group">
      <a 
        class="list-group-item" 
        *ngFor="let category of categoryList"
        (click)="getProducts(category.categoryId)">
        {{category.categoryName}}
      </a>
    </div>
  </div>
  <div class="col-lg-9">
    <div class="list-group">
      <a 
        class="list-group-item" 
        *ngFor="let product of productsWithCategory">
        {{product.productName}}
      </a>
    </div>
  </div>
</div>

category.component.ts:

import { Component, OnInit, DoCheck } from '@angular/core';
import { CategoryService } from './category.service';
import { Category } from './category';
import { Product } from '../product/product';
import { ProductService } from '../product/product.service';

@Component({
  selector: 'app-category',
  templateUrl: './category.component.html',
  styleUrls: ['./category.component.css'],
  providers: [CategoryService, ProductService]
})
export class CategoryComponent implements OnInit {
  categoryList: Category[];
  productsWithCategory: Product[] = [];
  constructor(
    private categoryService: CategoryService, 
    private productService: ProductService
  ) {}

  ngOnInit() {
    this.getCategories();
  }

  getCategories() {
    return this.categoryService.getCategories().subscribe(p => this.categoryList = p);
  }

  getProducts(categoryId: number) {
    this.productsWithCategory = this.productService.getProductsByCategory(categoryId);
  }
}

product.service.ts:

import { Injectable, Inject } from '@angular/core';
import { Product } from './product';
import { ProductList } from './product-list.mock';
import { Http, Response } from '@angular/http'
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/do'
import 'rxjs/add/operator/catch'

@Injectable()
export class ProductService {
  constructor(
    private http: Http, 
    @Inject('apiUrl') private apiUrl
  ) {}

  productsByCategory: Product[] = [];
  getProducts(): Observable < Product[] > {
    return this.http.get(this.apiUrl + "products").map(response => response.json());
  }

  getProductsByCategory(categoryId: number) {
    this.getProducts().subscribe(x => this.productsByCategory = x.filter((s: Product) => s.categoryId == categoryId, x));

    **
    * //this.productsByCategory => is empty on first click***
    return this.productsByCategory;
  }
}

3
  • 6
    Because your return is called before subscribe when the code executes Commented Mar 16, 2018 at 14:29
  • A simple fix is to wait for categoryList and productsWithCategory with a ngIf(categoryList) Commented Mar 16, 2018 at 14:41
  • @David what do you mean? Because subsribe is async? Commented Mar 19, 2018 at 6:40

1 Answer 1

1

this.getProducts() is an asynchronous operation. So if you just return this.productsByCategory after calling that function, the call won't have finished. After that, of course, if you call the function, the asynchronous operation will have (probably) finished, and you'll receive the last data you retrieved. In fact, you could retrieve wrong results, as you always get the products filtered by the last call that finished, disregarding the actual categoryId you pass as parameter.

In fact, if you make several calls and they stop working (because you lose connection to the server) you keep 'receiving' data, though that data doesn't change.

You should return an Observable from getProductsByCategory:

return this.getProducts().filter(s => s.categoryId === categoryId);

Then you could subscribe to the observable in the calling function or use the async pipe in the template.

Sign up to request clarification or add additional context in comments.

2 Comments

thank you for explanation.when i use "this.getProducts().filter(s => s.categoryId === categoryId);", i get error like this: property filter does not exist on type observable<Product[]>
You must import the filter operator, like you import map or do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.