user3188649
user3188649

Reputation: 139

Angular 5 fill empty array with httpget

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

Upvotes: 0

Views: 599

Answers (1)

Oscar Paz
Oscar Paz

Reputation: 18292

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.

Upvotes: 1

Related Questions