0

Code of my product-list component is as follows:

import {ChangeDetectorRef, Component} from '@angular/core';
import {ProductsListService} from './products-list.service';

@Component({
  selector: 'app-products-list',
  templateUrl: './products-list.component.html',
  styleUrls: ['./products-list.component.css']
})
export class ProductsListComponent {
  rerender = false;
  products;
  constructor(private service: ProductsListService, private cdRef: ChangeDetectorRef) {
    this.products = service.getProducts();
  }
  getProductsAgain(query: string): void {
    this.products = this.service.getProducts(query);
    console.log(this.products);
    this.doRerender();
  }
  // tslint:disable-next-line:typedef
  doRerender() {
    this.rerender = true;
    this.cdRef.detectChanges();
    this.rerender = false;
  }
}

and html looks like this:

<ng-container *ngIf="!rerender">
  <ul>
    <li *ngFor="let product of products">
      <img src="{{product.picture}}" height="150px">
      <p>{{product.title}}</p>
      <div>
        <p>{{"ENERC_KCAL: "+product.nutrients._ENERC_KCAL}}</p>
        <p>{{"PROCNT: "+product.nutrients._PROCNT}}</p>
        <p>{{"FAT: "+product.nutrients._FAT}}</p>
        <p>{{"CHOCDF: "+product.nutrients._CHOCDF}}</p>
        <p>{{"FIBTG: "+product.nutrients._FIBTG}}</p>
      </div>
    </li>
  </ul>
</ng-container>

also product-list.service looks like this:

import {Injectable, Optional} from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {ResponseDTO} from './ResponseDTO';
import {NutrientsDTO} from './NutrientsDTO';

@Injectable({
  providedIn: 'root'
})
export class ProductsListService {
  constructor(private http: HttpClient) {
  }

  public getProducts(q?: string): Array<ResponseDTO> {
    const query = (q === undefined) || (q == null) || (q === '') || (q === ' ') ? 'orange' : q;
    const url = 'https://edamam-food-and-grocery-database.p.rapidapi.com/parser?ingr=' + query;
    const httpHeaders = new HttpHeaders({
      'content-type': 'application/json',
      'x-rapidapi-key': '9f95da4cd3mshd0fbea030c7c8e3p16d06bjsn503efa8f187f',
      'x-rapidapi-host': 'edamam-food-and-grocery-database.p.rapidapi.com'
    });
    const arr: Array<any> = [];
    const res: Array<ResponseDTO> = [];
    this.http.get(url, {headers: httpHeaders, observe: 'body', responseType: 'json'}).subscribe(data => {
        const obj = JSON.parse(JSON.stringify(data));
        arr.push(obj.hints);
        for (const ar of arr) {
          const nutrients: NutrientsDTO = new NutrientsDTO();
          for (const a of ar) {
            // tslint:disable-next-line:forin
            for (const nutrient in a.food.nutrients) {
              if (a.food.nutrients.hasOwnProperty(nutrient)) {
                if (nutrient === 'ENERC_KCAL') {
                  nutrients.ENERC_KCAL = Math.round((a.food.nutrients[nutrient] + Number.EPSILON) * 100) / 100;
                } else if (nutrient === 'PROCNT') {
                  nutrients.PROCNT = Math.round((a.food.nutrients[nutrient] + Number.EPSILON) * 100) / 100;
                } else if (nutrient === 'FAT') {
                  nutrients.FAT = Math.round((a.food.nutrients[nutrient] + Number.EPSILON) * 100) / 100;
                } else if (nutrient === 'CHOCDF') {
                  nutrients.CHOCDF = Math.round((a.food.nutrients[nutrient] + Number.EPSILON) * 100) / 100;
                } else if (nutrient === 'FIBTG') {
                  nutrients.FIBTG = Math.round((a.food.nutrients[nutrient] + Number.EPSILON) * 100) / 100;
                } else {
                  console.log('not found!');
                }
              }
            }
            const noImgUrl = 'https://st4.depositphotos.com/14953852/24787/v/600/depositphotos_247872612-stock-illustration-no-image-available-icon-vector.jpg';
            const imgSrc = a.food.image == null ? noImgUrl : a.food.image;
            const response: ResponseDTO = new ResponseDTO(imgSrc, a.food.category, a.food.label, nutrients);
            res.push(response);
          }
        }
      }
    );
    return res;
  }
}

I am calling product-list method getProductsAgain by different component search-bar, which simply goes back to the service and calls API again with given parameter. The call is successful and it also assigns value to product variable of ProductsListComponent, but page does not refresh or re-render html. What should I do? or what am I doing wrong?

UPDATE #1:

Still have same problem, I tried all methods like trackBy, [...arrayOfResults], returned Subscription or Observable in component and got result in there, inspected result with setTimeout(()=>{console.log(products)},5000); to see if API request really assigns new values to products variable (and it does!). I even tried to force re-render with ChangeDetectorRef.detectChanges() but no success. So with API request is nothing wrong, but for some reason angular does not see that I update with API request products variable and its stuck on initial results (results in service.getProducts()). What am I doing so wrong?

5
  • Your constructor should almost always be empty. You should use ngOnInit() instead. Commented Apr 10, 2021 at 16:52
  • You need to show us what service.getProducts() looks like. It might be returning the wrong thing. Commented Apr 10, 2021 at 17:56
  • it returns Array<ResponseDTO> array list of object where I map JSON response values to object variables. I will update question with more info Commented Apr 10, 2021 at 18:18
  • 1
    Your code returns the array before the http request even finishes, thus your function is returning an empty array. Use a subscription and return the subscription. Commented Apr 10, 2021 at 18:41
  • but how come when I console.log(this.products) after API call i do get all necessary data? Commented Apr 11, 2021 at 11:34

2 Answers 2

1

you can try like this as follow ?; i didnt try it but it is might be all right.

getProductsAgain(query: string): void {
    this.service.getProductsAgain(query).subscribe(response => {
        this.products = response.data;
        console.log(this.products);
        this.doRerender();
      }, error => {
        console.log(error);
      });
}
Sign up to request clarification or add additional context in comments.

1 Comment

It failed to compile because I cannot subscribe to non observable array.
0

I am thinking it has to do with the fact that arrays and objects in JavaScript are reference types and you have to change the location of RAM for these type of variables for change detection to take place.

Try this:

getProductsAgain(query: string): void {
    // assign this.products to a new location in RAM/memory for change detection to take place
    // by using the spread operator and assigning to a new array
    this.products = [...this.service.getProducts(query)];
    // console.log(this.products); I don't think you need these two lines anymore
    // this.doRerender();
  }

Comments

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.