Reputation: 412
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?
Upvotes: 10
Views: 38461
Reputation: 617
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>
Upvotes: -1
Reputation: 57939
@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)
Upvotes: 6
Reputation: 409
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.
Upvotes: 6