Tom Aalbers Tom Aalbers - 2 months ago 21
TypeScript Question

Angular2: How to load data before rendering the component?

I am trying to load an event from my API before the component gets rendered. Currently I am using my API service which I call from the ngOnInit function of the component.

My

EventRegister
component:

import {Component, OnInit, ElementRef} from "angular2/core";
import {ApiService} from "../../services/api.service";
import {EventModel} from '../../models/EventModel';
import {Router, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig, RouteParams, RouterLink} from 'angular2/router';
import {FORM_PROVIDERS, FORM_DIRECTIVES, Control} from 'angular2/common';

@Component({
selector: "register",
templateUrl: "/events/register"
// provider array is added in parent component
})

export class EventRegister implements OnInit {
eventId: string;
ev: EventModel;

constructor(private _apiService: ApiService,
params: RouteParams) {
this.eventId = params.get('id');
}

fetchEvent(): void {
this._apiService.get.event(this.eventId).then(event => {
this.ev = event;
console.log(event); // Has a value
console.log(this.ev); // Has a value
});
}

ngOnInit() {
this.fetchEvent();
console.log(this.ev); // Has NO value
}
}


My
EventRegister
template

<register>
Hi this sentence is always visible, even if `ev property` is not loaded yet
<div *ngIf="ev">
I should be visible as soon the `ev property` is loaded. Currently I am never shown.
<span>{{event.id }}</span>
</div>
</register>


My API service

import "rxjs/Rx"
import {Http} from "angular2/http";
import {Injectable} from "angular2/core";
import {EventModel} from '../models/EventModel';

@Injectable()
export class ApiService {
constructor(private http: Http) { }
get = {
event: (eventId: string): Promise<EventModel> => {
return this.http.get("api/events/" + eventId).map(response => {
return response.json(); // Has a value
}).toPromise();
}
}
}


The component gets rendered before the API call in the
ngOnInit
function is done fetching the data. So I never get to see the event id in my view template. So it looks like this is a ASYNC problem. I expected the binding of the
ev
(
EventRegister
component) to do some work after the
ev
property was set. Sadly it does not show the
div
marked with
*ngIf="ev"
when the property gets set.

Question: Am I using a good approach? If not; What is the best way to load data before the component is starting to render?

NOTE: The
ngOnInit
approach is used in this angular2 tutorial.

EDIT:

Two possible solutions. First was to ditch the
fetchEvent
and just use the API service in the
ngOnInit
function.

ngOnInit() {
this._apiService.get.event(this.eventId).then(event => this.ev = event);
}


Second solution. Like the answer given.

fetchEvent(): Promise<EventModel> {
return this._apiService.get.event(this.eventId);
}

ngOnInit() {
this.fetchEvent().then(event => this.ev = event);
}

Answer

update

original

When console.log(this.ev) is executed after this.fetchEvent();, this doesn't mean the fetchEvent() call is done, this only means that it is scheduled. When console.log(this.ev) is executed, the call to the server is not even made and of course has not yet return a value.

Change fetchEvent() to return a Promise

 fetchEvent(): void {
    return  this._apiService.get.event(this.eventId).then(event => {
        this.ev = event;
        console.log(event); // Has a value
        console.log(this.ev); // Has a value
    });
 }

change ngOnInit() to wait for the Promise to complete

ngOnInit() {
    this.fetchEvent().then(() =>
    console.log(this.ev)); // Now has value;
}

This actually won't buy you much for your use case.

My suggestion: Wrap your entire template in an <div *ngIf="isDataAvailable"> (template content) </div>

and in ngOnInit()

isDataAvailable:boolean = false;

ngOnInit() {
    this.fetchEvent().then(() =>
    this.isDataAvailable = true); // Now has value;
}