In my Angular 2 application, I get an error:
Cannot read property 'title' of undefined.
This is a very simple component, just trying to get a bare minimum to work here. It hits my API controller (curiously multiple times), and it appears to hit the callback after an object is returned. My console.log outputs the object I would expect. Here is the full error:
TypeError: Cannot read property 'title' of undefined
at AbstractChangeDetector.ChangeDetector_About_0.detectChangesInRecordsInternal (eval at <anonymous> (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:10897:14), <anonymous>:31:26)
at AbstractChangeDetector.detectChangesInRecords (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8824:14)
at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8807:12)
at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8877:14)
at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8811:12)
at AbstractChangeDetector._detectChangesContentChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8871:14)
at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8808:12)
at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8877:14)
at AbstractChangeDetector.runDetectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8811:12)
at AbstractChangeDetector.detectChanges (http://localhost:55707/lib/angular2/bundles/angular2.dev.js:8796:12)
The service (about.service.ts):
import {Http} from 'angular2/http';
import {Injectable} from 'angular2/core';
import {AboutModel} from './about.model';
import 'rxjs/add/operator/map';
@Injectable()
export class AboutService {
constructor(private _http: Http) { }
get() {
return this._http.get('/api/about').map(res => {
console.log(res.json()); // I get the error on the line above but this code is still hit.
return <AboutModel>res.json();
});
}
}
The Component (about.component.ts):
import {Component, View, OnInit} from 'angular2/core';
import {AboutModel} from './about.model';
import {AboutService} from './about.service';
import {HTTP_PROVIDERS} from 'angular2/http';
@Component({
selector: 'about',
providers: [HTTP_PROVIDERS, AboutService],
templateUrl: 'app/about/about.html'
})
export class About implements IAboutViewModel, OnInit {
public about: AboutModel;
constructor(private _aboutService: AboutService) {}
ngOnInit() {
this._aboutService.get().subscribe((data: AboutModel) => {
this.about = data;
});
}
}
export interface IAboutViewModel {
about: AboutModel;
}
index.html
<script src="~/lib/systemjs/dist/system.src.js"></script>
<script src="~/lib/angular2/bundles/router.js"></script>
<script src="~/lib/angular2/bundles/http.js"></script>
<script src="~/lib/angular2/bundles/angular2-polyfills.js"></script>
<script src="~/lib/angular2/bundles/angular2.dev.js"></script>
<script src="~/lib/es6-shim/es6-shim.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
}
},
map: {
rxjs: "lib/rxjs"
}
});
System.import('app/boot')
.then(null, console.error.bind(console));
</script>
To solve the "Cannot read properties of undefined" error, make sure that the DOM element you are accessing exists. The error is often thrown when trying to access a property at a non-existent index after using the getElementsByClassName() method. Copied! const boxes = document.
What Causes TypeError: Cannot Read Property of Undefined. Undefined means that a variable has been declared but has not been assigned a value. In JavaScript, properties and functions can only belong to objects.
To fix Error: TypeError: Cannot read property '…' of undefined with Angular, we should make sure the variable we're rendering is defined. to use the safe navigation operator if foo and bar are objects. We use arr && arr. length > 0 to make sure arr is defined and it has length bigger than 0.
The "Cannot read property 'value' of null" error occurs when: trying to access the value property on a null value, e.g. after calling getElementById with an invalid identifier. inserting the JS script tag before the DOM elements have been declared.
Please include your view and model next time (app/about/about.html and about.model).
If you are returning an array, you can use the asyncPipe, which "subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes" hence the view will be updated with the new value.
If you are returning a primitive type (string, number, boolean) you can also use the asyncPipe.
If you are returning an object, I'm not aware of any way to use asyncPipe, we could use the async pipe, in conjunction with the safe navigation operator ?.
as follows:
{{(objectData$ | async)?.name}}
But that looks a bit complicated, and we'd have to repeat that for each object property we wanted to display.
As @pixelbits mentioned in a comment, you can subscribe()
to the observable in the controller and store the contained object into a component property. Then use the safe navigation operator or NgIf in the template:
service.ts
import {Injectable} from 'angular2/core';
import {Http} from 'angular2/http';
import 'rxjs/add/operator/map'; // we need to import this now
@Injectable()
export class MyService {
constructor(private _http:Http) {}
getArrayData() {
return this._http.get('./data/array.json')
.map(data => data.json());
}
getPrimitiveData() {
return this._http.get('./data/primitive.txt')
.map(data => data.text()); // note .text() here
}
getObjectData() {
return this._http.get('./data/object.json')
.map(data => data.json());
}
}
app.ts
@Component({
selector: 'my-app',
template: `
<div>array data using '| async':
<div *ngFor="let item of arrayData$ | async">{{item}}</div>
</div>
<div>primitive data using '| async': {{primitiveData$ | async}}</div>
<div>object data using .?: {{objectData?.name}}</div>
<div *ngIf="objectData">object data using NgIf: {{objectData.name}}</div>`
providers: [HTTP_PROVIDERS, MyService]
})
export class AppComponent {
constructor(private _myService:MyService) {}
ngOnInit() {
this.arrayData$ = this._myService.getArrayData();
this.primitiveData$ = this._myService.getPrimitiveData();
this._myService.getObjectData()
.subscribe(data => this.objectData = data);
}
}
data/array.json
[ 1,2,3 ]
data/primitive.json
Greetings SO friends!
data/object.json
{ "name": "Mark" }
Output:
array data using '| async':
1
2
3
primitive data using '| async': Greetings SO friends!
object data using .?: Mark
object data using NgIf: Mark
Plunker
It looks like you have referred to about.title
in the view about.html
but the about
variable is instantiated only after the http
request is completed. To avoid this error you can wrap about.html
with <div *ngIf="about"> ... </div>
The previous answer is correct. You need to check if the variable is defined before use it in your template. Using HTTP request it need time to define it. use *ngIf to check. Example is provided from angular with https://angular.io/docs/ts/latest/tutorial/toh-pt5.html and example is http://plnkr.co/edit/?p=preview
<div *ngIf="hero">
<h2>{{hero.name}} details!</h2>
<div>
You can check app/hero-detail.component [ts and html]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With