Angular2 - TypeError: Cannot read property "Id" from undefined in (Typescript)

I get the following error:

angular2.dev.js:23925 EXCEPTION: TypeError: Cannot read property 'Id' of null in [ {{ product.Id }} in ProductEditComponent@0 :68] 

thrown using:

 //Product-edit.component.ts: import {Component } from 'angular2/core'; import { IProduct } from './product' import { ProductService } from './product.service' import { RouteParams } from 'angular2/router'; @Component({ template:`<div class="wrapper wrapper-content animated fadeInRight ecommerce"> {{ product.Id }} </div>`, }) export class ProductEditComponent{ product: IProduct = null; errorMessage: string; constructor(private _routeParams: RouteParams, private _productService: ProductService){ } ngOnInit(): void { this._productService.getProduct(this._routeParams.get('id')) .subscribe( product => this.product = product, error => this.errorMessage = <any>error); } } 

ProductService:

 getProduct(id: string): Observable<IProduct> { return this._http.get(this._baseUrl + this._getProductUrl + '/' + id) .map((response: Response) => <IProduct>response.json()) .do(data => console.log("All: " + JSON.stringify(data))) .catch(this.handleError); } 

The response from the server:

 {"Id":"34d4efcy6","ExternalId":null,"UserId":"testing","ProductProvider":null,"Title":"Flaska vin","Desc":null,"MinDeliveryTime":null,"MaxDeliveryTime":null,"FreightCost":null,"Brand":null} 

What have I messed up?

+5
source share
1 answer

In your component, you initialize product zero, and then reference product.Id in your template. The error occurs when Angular tries to draw your template initially, before your asynchronous call returns - at what point the product is still null, so the error is: Cannot read property 'Id' of null .

The most immediate solution is to use the Elvis operator, which Angular provides for situations like this. You can use it by replacing {{ product.Id }} with {{ product?.Id }} in your template.

However, you are likely to run into problems detecting changes with this approach, and overall you will be much better off with an approach such as:

 export class ProductEditComponent{ product: Observable<IProduct>; //product is an Observable errorMessage: string; constructor(private _routeParams: RouteParams, private _productService: ProductService){ //product is always defined because you instantiate it in the ctor this._productService.getProduct(this._routeParams.get('id')); } 

Then you used {{(product | async).Id }} instead of {{product.Id}} in your template, using AsyncPipe , so that Angular would AsyncPipe with the subscription dirt and update the user interface as needed for you.

+20
source

All Articles