9
votes

I have a component in my angular5 application called: product-list.component.ts. In this component, I have a constructor, which calls a REST API.

product.service.ts

getAllProductsFromACategory(categoryName: string): any {
this.http.get('http://localhost:8080/VespaWebshopAPI
/api/Article/Category?categoryName=' + categoryName).
subscribe(data => {
var article: Article = {
    id: data[0].id,
    name: data[0].name,
    articleNr: data[0].articleNr,
    stock: data[0].stock,
    price: data[0].price,
    description: data[0].description
  };

  return article;
}); 

product-list.component.ts

public article: Article;

constructor(private route: ActivatedRoute, 
private productService: productService) { 
 //Call rest api
 this.article = 
 this.productService.getAllProductsFromACategory('Bremsen');
}

article.ts

export interface Article {
id : number;
name : string;
articleNr : string;
stock : number;
price : number;
description : string;
}

In my HTML, I want to display some properties of an article. So if I try to run this code in my product-list.component.html file, I get the following error:

{{ article.id }}

Error

ProductListComponent.html:1 ERROR TypeError: Cannot read property 'id' of undefined
at Object.eval [as updateRenderer] (ProductListComponent.html:1)
at Object.debugUpdateRenderer [as updateRenderer] (core.js:14727)
at checkAndUpdateView (core.js:13841)
at callViewAction (core.js:14187)
at execComponentViewsAction (core.js:14119)
at checkAndUpdateView (core.js:13842)
at callViewAction (core.js:14187)
at execEmbeddedViewsAction (core.js:14145)
at checkAndUpdateView (core.js:13837)
at callViewAction (core.js:14187)

The REST API works; I tested it. I guess there is just the problem that the data should get loaded first before the HTML page gets displayed. Somewhere else I read that in an ngFor, I should make use of the | async pipe, but that didn't work for me.

So how do I solve this?

3
@Jota.Toledo Doesn't work for me.ConanCode
Update your question to refute that it isn't a duplicateJota.Toledo

3 Answers

6
votes

@TNII, @Hoang Duc, try say you is that generally a service expose Observables. It's in a ngOnInit in your component when you subscribe to the Observable.

//Simple return a get
getAllProductsFromACategory(categoryName: string): any {
   return this.http.get('http://localhost:8080/VespaWebshopAPI
   /api/Article/Category?categoryName=' + categoryName)
}

In the component, generally in an ngOnInit when we subscribe to

ngOnInit()
{
    productService.getAllProductsFromACategory('Bremsen').
      subscribe(data => {
         if (data[0])
             this.article=data[0];
      })
}

the {{article?.id}} wrote in the html is a abreviate way to say: if this.article is defined, show me this.article.id.

check if when in a navigator write http://localhost:8080/VespaWebshopAPI /api/Article/Category?categoryName='Bremsen', give you an array of Articles. Check if the elements of the arrays has properties id,name,etc (or return element with another properties)

5
votes

In productService.getAllProductsFromACategory() replace subscribe with map so that the method returns Observable<Article>

In constructor of product-list.component.ts, subscribe to the Observable<Article> returned from productService and set value for this.article in that

this.productService.getAllProductsFromACategory('Bremsen')
    .subscribe((art: Article) => {
        this.article = art;
    },
    (err: any) => console.error(err));

In your html, put an *ngIf="article" in the surrounding element of your interpolation {{ article.id }} to prevent it from processing until article obtains a valid value.

-2
votes

The problem is that the template is trying to use a data which is not available yet. Before accessing it, the server's response must be completed. So, in order "to wait and avoid that error", i did a similar trick that worked fine.

I wrote a conditional directive on the surronding element. Doing that, "while data_from_server is null", template does not try to get the info and does not produce the fail.

<div style="text-align:center" *ngIf="data_from_server">
  <form>
    <label for="name">NAME</label> 
    <input [(ngModel)]="data_from_server.nombre" name="nombre">
  </form>
 </div>