I'm getting the following error:
angular2.dev.js:23925 EXCEPTION: TypeError: Cannot read property 'Id' of null in [
{{ product.Id }}
in ProductEditComponent@0:68]
thrown with:
//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);
}
Response from server:
{"Id":"34d4efcy6","ExternalId":null,"UserId":"testing","ProductProvider":null,"Title":"Flaska vin","Desc":null,"MinDeliveryTime":null,"MaxDeliveryTime":null,"FreightCost":null,"Brand":null}
What am I messing up?
In your component, you're initializing product
to null, and then referencing product.Id
in your template. The error occurs when Angular tries to draw your template initially, before your async call returns - at which point product
is still null, thus the error: Cannot read property 'Id' of null
.
The most immediate solution is to use the Elvis operator, which Angular provides for situations precisely like this. You'd use it by replacing {{ product.Id }}
with {{ product?.Id }}
in your template.
That said, you're likely to run into change detection problems with this approach, and in general you'll be much better off with an approach like:
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'));
}
You'd then use {{(product | async).Id }}
in place of {{product.Id}}
in your template, leveraging AsyncPipe
to let angular handle the dirtywork of subscribing and updating the UI as needed for you.