Reputation: 3407
I'm working on an angular app that uses Router that links to multiple component from AppComponent
. Here is a simplified version on plunker to illustrate the issue (for some reason, the exact same code runs locally but not on plunker.)
When the app starts from http://localhost:3000
or http://localhost:3000/home
, the links fruit1
and fruit2
works fine (clicking leads to URL changing to http://localhost:3000/fruit/1
with description text). However, the problem is that when starting on this url http://localhost:3000/fruit/1
(typing in this url and hit enter), it seems that the call to service inside fruit-detail.component.ts
's ngOnInit
is called AFTER trying to render its html component, which causes the stack trace below:
EXCEPTION: TypeError: Cannot read property 'description' of null in [ desc: {{fruit.description}} in FruitDetailComponent@1:6]
angular2.dev.js:23925 EXCEPTION: TypeError: Cannot read property 'description' of null in [ desc: {{fruit.description}} in FruitDetailComponent@1:6]BrowserDomAdapter.logError @ angular2.dev.js:23925BrowserDomAdapter.logGroup @ angular2.dev.js:23936ExceptionHandler.call @ angular2.dev.js:1320(anonymous function) @ angular2.dev.js:12857schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError @ angular2.dev.js:13666NgZoneImpl.inner.inner.fork.onHandleError @ angular2.dev.js:2143ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runGuarded @ angular2-polyfills.js:300NgZoneImpl.runInner @ angular2.dev.js:2155NgZone.run @ angular2.dev.js:13749(anonymous function) @ angular2.dev.js:12956schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._checkStable @ angular2.dev.js:13689NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.setMicrotask @ angular2.dev.js:13660NgZoneImpl.inner.inner.fork.onHasTask @ angular2.dev.js:2135ZoneDelegate.hasTask @ angular2-polyfills.js:449ZoneDelegate._updateTaskCount @ angular2-polyfills.js:466ZoneDelegate.invokeTask @ angular2-polyfills.js:427Zone.runTask @ angular2-polyfills.js:320drainMicroTaskQueue @ angular2-polyfills.js:541ZoneTask.invoke @ angular2-polyfills.js:493
angular2.dev.js:23925 ORIGINAL EXCEPTION: TypeError: Cannot read property 'description' of nullBrowserDomAdapter.logError @ angular2.dev.js:23925ExceptionHandler.call @ angular2.dev.js:1329(anonymous function) @ angular2.dev.js:12857schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError @ angular2.dev.js:13666NgZoneImpl.inner.inner.fork.onHandleError @ angular2.dev.js:2143ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runGuarded @ angular2-polyfills.js:300NgZoneImpl.runInner @ angular2.dev.js:2155NgZone.run @ angular2.dev.js:13749(anonymous function) @ angular2.dev.js:12956schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._checkStable @ angular2.dev.js:13689NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.setMicrotask @ angular2.dev.js:13660NgZoneImpl.inner.inner.fork.onHasTask @ angular2.dev.js:2135ZoneDelegate.hasTask @ angular2-polyfills.js:449ZoneDelegate._updateTaskCount @ angular2-polyfills.js:466ZoneDelegate.invokeTask @ angular2-polyfills.js:427Zone.runTask @ angular2-polyfills.js:320drainMicroTaskQueue @ angular2-polyfills.js:541ZoneTask.invoke @ angular2-polyfills.js:493
angular2.dev.js:23925 ORIGINAL STACKTRACE:BrowserDomAdapter.logError @ angular2.dev.js:23925ExceptionHandler.call @ angular2.dev.js:1332(anonymous function) @ angular2.dev.js:12857schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.onError @ angular2.dev.js:13666NgZoneImpl.inner.inner.fork.onHandleError @ angular2.dev.js:2143ZoneDelegate.handleError @ angular2-polyfills.js:394Zone.runGuarded @ angular2-polyfills.js:300NgZoneImpl.runInner @ angular2.dev.js:2155NgZone.run @ angular2.dev.js:13749(anonymous function) @ angular2.dev.js:12956schedulerFn @ angular2.dev.js:13264SafeSubscriber.__tryOrUnsub @ Rx.js:10775SafeSubscriber.next @ Rx.js:10730Subscriber._next @ Rx.js:10690Subscriber.next @ Rx.js:10667Subject._finalNext @ Rx.js:11191Subject._next @ Rx.js:11183Subject.next @ Rx.js:11142EventEmitter.emit @ angular2.dev.js:13245NgZone._checkStable @ angular2.dev.js:13689NgZone._zoneImpl.ng_zone_impl_1.NgZoneImpl.setMicrotask @ angular2.dev.js:13660NgZoneImpl.inner.inner.fork.onHasTask @ angular2.dev.js:2135ZoneDelegate.hasTask @ angular2-polyfills.js:449ZoneDelegate._updateTaskCount @ angular2-polyfills.js:466ZoneDelegate.invokeTask @ angular2-polyfills.js:427Zone.runTask @ angular2-polyfills.js:320drainMicroTaskQueue @ angular2-polyfills.js:541ZoneTask.invoke @ angular2-polyfills.js:493
angular2.dev.js:23925 TypeError: Cannot read property 'description' of null
at AbstractChangeDetector.ChangeDetector_FruitDetailComponent_0.detectChangesInRecordsInternal (viewFactory_FruitDetailComponent:30)
at AbstractChangeDetector.detectChangesInRecords (angular2.dev.js:9689)
at AbstractChangeDetector.runDetectChanges (angular2.dev.js:9672)
at AbstractChangeDetector._detectChangesInViewChildren (angular2.dev.js:9751)
at AbstractChangeDetector.runDetectChanges (angular2.dev.js:9676)
at AbstractChangeDetector._detectChangesContentChildren (angular2.dev.js:9745)
at AbstractChangeDetector.runDetectChanges (angular2.dev.js:9673)
at AbstractChangeDetector._detectChangesInViewChildren (angular2.dev.js:9751)
at AbstractChangeDetector.runDetectChanges (angular2.dev.js:9676)
at AbstractChangeDetector.detectChanges (angular2.dev.js:9661)
And after that, this is logged to the console, which is why i suspect ngOnInit is called after. So how to have a working app when even loading directly from 'fruit/1' url? Any help is appreciated! Thanks!
===== extract data called!!
fruit-detail.component.ts:25 got fruit inside fruitComponent !apple
Upvotes: 2
Views: 1968
Reputation: 202176
You could use the elvis operator for this:
<h2> desc: {{fruit?.description}} </h2>
In fact, the call this._foodService.getData()
is executed asynchronously and you get the result after the ngOnInit
method returns.
Since you use routing I think that the OnActivate
interface and its routerOnActivate
method is what you're looking for. If you return a promise within this method, the component will wait for the promise to be resolved before changing the route. So when the component will be displayed the data will be there and you don't have such problem.
Here is a sample:
@Component({
(...)
})
export class FruitDetailComponent implements OnInit, OnActivate {
fruit = null;
constructor(
private _rbacService: FoodService,
private _routeParams: RouteParams) {}
routerOnActivate(next: ComponentInstruction,
prev: ComponentInstruction) {
return new Promise(resolve => {
let id = +this._routeParams.get('id');
console.log('on init fruit component: id = '+id);
//get read write roles
this._rbacService.getData().subscribe(data =>{
this.fruit = data['fruits'].filter(obj => obj.id === id)[0];
console.log('got fruit inside fruitComponent '+this.fruit.description);
resolve(null);
})
});
}
}
See this link for more details:
Upvotes: 2