Search code examples
angularapiservicecomponentsupdating

Angular Component view not using the latest API response from service


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.


Solution

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