user5021816 user5021816 - 1 month ago 174
TypeScript Question

Angular 2 ngOnInit Called Twice With Error

I ran into a strange error in Angular 2. I have two similar components with similar templates and similar services. Here is the basics of how they work:

Component:

theData: any;

constructor(private _theService: TheService) {}

ngOnInit() {
this._theService.getData()
.subscribe(data => {
this.theData = data;
});
}


Service:

private _url = "http://url.net/api/Data";

constructor(private _http: Http) {
}

getData() {
return this._http.get(this._url)
.map(res => res.json());
}


this returns an array of objects, like:

[
{name: "Name 1"},
{name: "Name 2"}
]


and the Template displays them like:

<div *ngFor="let item of theData">{{ item.name }}</div>


Then you can click into an item where you are taken to a page that follows the same strategy as above, except the service/api only returns a single object and the template does not use *ngFor because there is only one item to show.

So it looks like:

theItem = {name: "Name 1"} // what the service returns

<div>{{ theItem.name }}</div>


Everything works great when displaying all items, but when I click into the individual items, OnInit is called twice and I get an error in platform-browser.umd.js which is really an error in the template because it says the it can't read the property
name
of
undefined
(which is theItem).

I solved this problem by changing the template for individual items to:

<div>{{ theItem?.name }}</div>


At first I thought the problem was that the template was rendering before the data had loaded from the service, so adding
?
fixed it. But shouldn't the template be loading AFTER ngOnInit has finished? My question is, why did adding the
?
fix this problem, even though I don't need it in the first template when loading all items?

Thanks.

Answer

Because, the get is asynchronous, it does in fact begin to render the template before the data is loaded. It will fire off the service call and won't wait, it will continue processing. Adding the ? allows it to be undefined and then render appropriately once the data is loaded.