I have an angular2 project as follows:
package.json:
{ "name": "self-assessment", "version": "1.0.0", "scripts": { "postinstall": "npm run typings install", "lite": "lite-server", "gulp": "gulp", "start": "concurrent \"npm run gulp\" \"npm run lite\" ", "typings" : "typings" }, "license": "ISC", "dependencies": { "angular2": "2.0.0-beta.3", "systemjs": "0.19.6", "es6-promise": "^3.0.2", "es6-shim": "^0.33.3", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.0", "zone.js": "0.5.11" }, "devDependencies": { "autoprefixer": "^6.2.1", "cssnano": "^3.4.0", "concurrently": "^1.0.0", "gulp": "^3.9.0", "gulp-ext-replace": "^0.2.0", "gulp-imagemin": "^2.4.0", "gulp-postcss": "^6.0.1", "gulp-sourcemaps": "^1.6.0", "gulp-typescript": "^2.10.0", "gulp-uglify": "^1.5.1", "lite-server": "^2.0.1", "postcss": "^5.0.13", "postcss-scss": "^0.1.3", "precss": "^1.3.0", "typings":"^0.6.8", "tsd": "^0.6.5-beta" } }
app.component.ts:
import {Component} from 'angular2/core'; import {AssessmentListComponent} from "./assessment-list.component"; import {HomeComponent} from "./home.component"; import {ApiService} from "../services/api.service"; import {RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS} from 'angular2/router'; import {AssessmentComponent} from "./assessment.component"; @Component({ selector: 'my-app', templateUrl: 'app/components/app.component.html', directives: [ ROUTER_DIRECTIVES, ], providers: [ ApiService, ROUTER_PROVIDERS, ] }) @RouteConfig([ {path: '/home', name: 'Home', component: HomeComponent, useAsDefault: true}, {path: '/assessments', name: 'Assessments', component: AssessmentListComponent}, {path: '/assessment/:id', name: 'Assessment', component: AssessmentComponent}, ]) export class AppComponent { title = "Welcome to Self-Assessment" }
assessment.component.ts:
import {Component, OnInit} from 'angular2/core'; import {AssessmentModel} from '../models/assessment.model'; import {RouteParams} from 'angular2/router'; import {ApiService} from '../services/api.service'; @Component({ selector: 'assessment-component', templateUrl: 'app/components/assessment.component.html', }) export class AssessmentComponent implements OnInit{ assessment: AssessmentModel; errorMessage: any; constructor( private _apiService: ApiService, private _routeParams: RouteParams ) {} ngOnInit():any { let id = +this._routeParams.get('id'); this.getAssessment(id); } private getAssessment(id: number) { this._apiService.getAssessment(id) .subscribe( assessment => this.assessment = assessment, error => this.errorMessage = <any>error ); } goBack() { console.log(this.assessment); } }
api.service.ts:
import {Injectable} from 'angular2/core'; import {Http, Response} from 'angular2/http'; import {Observable} from 'rxjs/Observable'; @Injectable() export class ApiService { constructor(private _http: Http) {} private _baseApiUrl = 'http://localhost:8000/'; getAssessments() { return this.getObjectList('assessments', 'json'); } getAssessment(id: number) { return this.getObject('assessments', id, 'json'); } getObjectList(listName:string, listExtension: string) { var url = this._baseApiUrl + listName + '.' + listExtension; return this._http.get(url) .map(response => response.json()) .catch(this.handleError); } getObject(objectName:string, objectId:number, objectExtension: string) { var url = this._baseApiUrl + objectName + '/' + objectId + '.' + objectExtension; return this._http.get(url) .map(response => response.json()) .catch(this.handleError); } private handleError(error: Response) { console.error(error); return Observable.throw(error.json().error || 'Server error'); } }
and finally assessment.component.html:
<h3>{{ assessment.id }}</h3> <button (click)="goBack()">Go Back 2</button>
When the page is called with the URL: http: // localhost: 3000 / assessment / 4 , it calls the corresponding calls in the back-end twice and generates the following errors in the console:
Uncaught EXCEPTION: TypeError: Cannot read property 'id' of undefined in [{{ assessment.id }} in AssessmentComponent@0 :4] ORIGINAL EXCEPTION: TypeError: Cannot read property 'id' of undefined ORIGINAL STACKTRACE: TypeError: Cannot read property 'id' of undefined at AbstractChangeDetector.ChangeDetector_AssessmentComponent_0.detectChangesInRecordsInternal (viewFactory_AssessmentComponent:30:28) at AbstractChangeDetector.detectChangesInRecords (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8116:14) at AbstractChangeDetector.runDetectChanges (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8099:12) at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8184:14) at AbstractChangeDetector.runDetectChanges (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8103:12) at AbstractChangeDetector._detectChangesContentChildren (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8178:14) at AbstractChangeDetector.runDetectChanges (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8100:12) at AbstractChangeDetector._detectChangesInViewChildren (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8184:14) at AbstractChangeDetector.runDetectChanges (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8103:12) at AbstractChangeDetector.detectChanges (http://localhost:3000/node_modules/angular2/bundles/angular2.dev.js:8088:12) ERROR CONTEXT: [object Object]ExceptionHandler.call @ angular2.dev.js:1206(anonymous function) @ angular2.dev.js:12591NgZone._notifyOnError @ angular2.dev.js:13635collection_1.StringMapWrapper.merge.onError @ angular2.dev.js:13539Zone.run @ angular2-polyfills.js:1247NgZone._notifyOnTurnDone @ angular2.dev.js:13450(anonymous function) @ angular2.dev.js:13565zoneBoundFn @ angular2-polyfills.js:1220lib$es6$promise$asap$$flush @ angular2-polyfills.js:262
So it looks like {{evaluation.id}} is trying to render before the object is returned from an external server. If I then click the "Back 2" button, it will accordingly manage the object’s log:
{"id":4,"title":"Assessment Title here!","description":"Here is a description","questions":["http://localhost:8000/questions/1.json","http://localhost:8000/questions/2.json","http://localhost:8000/questions/3.json","http://localhost:8000/questions/4.json","http://localhost:8000/questions/5.json","http://localhost:8000/questions/6.json","http://localhost:8000/questions/7.json","http://localhost:8000/questions/8.json","http://localhost:8000/questions/9.json","http://localhost:8000/questions/10.json","http://localhost:8000/questions/11.json","http://localhost:8000/questions/12.json","http://localhost:8000/questions/13.json"],"responses":[]}
“It’s interesting that I don’t see problems in other places of the application (that is, I can easily download and display grades lists, etc.). In addition, I have the same problem in the Ionic2 application that I worked with last night as a training project.
My ass was chopped off, I hope someone knows the answer to this riddle.
Thanks!