0

I have a Parent Component (Navbar) that passes an @Input into my Child Component (Product).

The Navbar Component has an event subscribed to clicks to determine if an element on the Nav is being clicked. The Text from the clicked Nav element is passed into the Child Component via the @Input. i.e. Product selected was Beer => @Input productType = 'Beer'

<product [productType]="selectedProduct"></product>

The Product Component implements ngOnChanges and this is where I'm using the passed in 'productType' to call my service with the correct string.

ngOnChanges(changes: SimpleChanges) {
    this.result = this.productService.getProducts(changes.productType.currentValue);    
  }

The product service is being called with what is expected each time the nav items are clicked, but the component always seems to render the previous result instead of the current one.

My service call is as follows:

getProducts(productType: string): Observable<any> {
  this.http.get(this.baseUrl + 'api/test/' + productType).subscribe(result => {
    this.result = result;
  }, error => console.error(error));  
  return this.result;
}

I've put console.logs nearly everywhere to try and track this down but I can't seem to understand why the view only seems to be updated using the previous service calls response instead of the one that has just been triggered.

The product component view can be seen below:

<p class="info" *ngIf="!result">Please choose a product</p>
<ul class="products" *ngIf="result">
  <li class="product" *ngFor="let item of result.Items">
    <div class="productName">
      <p>{{item.Name}}</p>
    </div>
    <img src="{{ item.Image }}">
    <div class="productPrice"><p>£{{ item.Price }}</p></div>
  </li>
</ul>

I've tried moving the service call into the parent component and inlined the child component. Even with everything local in the Navbar component, I still get the same behaviour.

I've read a lot about the ngOnChange event and believe this is working as expected. I've played about with flags to try and force the view to rerender but this did not work either.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • In the sense that I'm missing a promise on the Service call? – user3459764 Dec 31 '19 at 16:16
  • In the sense that `return this.result` is *outside* the callback, so will be executed before the request completes. You don't have to use promises to solve this, observables can do it too, but you do need to deal with the fact that the operation is asynchronous. – jonrsharpe Dec 31 '19 at 16:16
  • Yes - that was the issue. Thanks for the help :) – user3459764 Dec 31 '19 at 16:32
  • I'd recommend running through https://angular.io/tutorial, which includes how to use asynchronous services. – jonrsharpe Dec 31 '19 at 16:33

1 Answers1

0

Here getProducts method is asynchronous which means you are returning result before it is even fetched/resolved/api completes. So to fix you can subscribe inside ngOnChanges and change your service to return Observable instead

 getProducts(productType: string): Observable<any> {
      return this.http.get(this.baseUrl + 'api/test/' + productType);
 }

ngOnChanges(changes: SimpleChanges) {
    this.productService.getProducts(changes.productType.currentValue).subscribe((result) => this.result = result);    
}
Siddharth Pal
  • 1,408
  • 11
  • 23
  • Fantastic! I knew it was something simple. I'd attempted to subscribe from the child component but browser was throwing errors stating that subscribe was not a function. Thanks so much for the help! I'm a complete noob on here so can't mark this as the answer but will update the question to mention that this solved my issue. – user3459764 Dec 31 '19 at 16:22