Angular2: ¿cómo cargar datos antes de representar el componente?

Estoy intentando cargar un evento desde mi API antes de que se represente el componente. Actualmente estoy usando mi servicio API al que llamo desde la función ngOnInit del componente.

Mi componente EventRegister :

 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 } } 

Mi plantilla EventRegister

  Hi this sentence is always visible, even if `ev property` is not loaded yet 
I should be visible as soon the `ev property` is loaded. Currently I am never shown. {{event.id }}

Mi servicio API

 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 => { return this.http.get("api/events/" + eventId).map(response => { return response.json(); // Has a value }).toPromise(); } } } 

El componente se renderiza antes de que la llamada API en la función ngOnInit se ngOnInit los datos. Así que nunca puedo ver la identificación del evento en mi plantilla de vista. Entonces parece que este es un problema ASYNC. Esperaba que el enlace del componente ev ( EventRegister ) hiciera algún trabajo después de que se estableciera la propiedad ev . Lamentablemente, no muestra el div marcado con *ngIf="ev" cuando se establece la propiedad.

Pregunta: ¿Estoy usando un buen enfoque? Si no; ¿Cuál es la mejor forma de cargar datos antes de que el componente comience a procesarse?

NOTA: El enfoque ngOnInit se utiliza en este tutorial angular2 .

EDITAR:

Dos posibles soluciones Primero fue abandonar el fetchEvent y simplemente usar el servicio API en la función ngOnInit .

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

Segunda solución Como la respuesta dada.

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

actualizar

  • Si usa el enrutador, puede usar enganches o soluciones de ciclo de vida para retrasar la navegación hasta que lleguen los datos. https://angular.io/guide/router#milestone-5-route-guards

  • Para cargar datos antes de la representación inicial del componente raíz, puede usar APP_INITIALIZER Cómo pasar parámetros procesados ​​desde el backend al método de bootstrap angular2

original

Cuando console.log(this.ev) se ejecuta después de this.fetchEvent(); , esto no significa que la llamada fetchEvent() está hecha, esto solo significa que está progtwigda. Cuando se ejecuta console.log(this.ev) , la llamada al servidor ni siquiera se realiza y, por supuesto, aún no ha devuelto un valor.

Cambiar fetchEvent() para devolver una Promise

  fetchEvent(){ 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 }); } 

cambie ngOnInit() para esperar a que se complete la Promise

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

Esto realmente no le comprará mucho para su caso de uso.

Mi sugerencia: Envuelva su plantilla completa en un

(template content)

y en ngOnInit()

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

Una buena solución que he encontrado es hacer en UI algo así como:

 
...Your page...

Solo cuando: isDataLoaded es verdadero, la página se representa.

Puede prearchivar sus datos utilizando Resolvers en Angular2 + , los Resolvers procesan sus datos antes de que su Componente se cargue por completo.

Hay muchos casos en los que desea cargar su componente solo si hay algo que está ocurriendo, por ejemplo, vaya a Dashboard solo si la persona que ya inició sesión, en este caso, los Resolvers son tan útiles.

Mira el diagtwig simple que he creado para ti de una de las maneras en que puedes usar el resolver para enviar los datos a tu componente.

enter image description here

Aplicar Resolver a tu código es bastante simple, creé los fragmentos para que veas cómo se puede crear el Resolver:

 import { Injectable } from '@angular/core'; import { Router, Resolve, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'; import { MyData, MyService } from './my.service'; @Injectable() export class MyResolver implements Resolve { constructor(private ms: MyService, private router: Router) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise { let id = route.params['id']; return this.ms.getId(id).then(data => { if (data) { return data; } else { this.router.navigate(['/login']); return; } }); } } 

y en el módulo :

 import { MyResolver } from './my-resolver.service'; @NgModule({ imports: [ RouterModule.forChild(myRoutes) ], exports: [ RouterModule ], providers: [ MyResolver ] }) export class MyModule { } 

y puede acceder a él en su componente de esta manera:

 ///// ngOnInit() { this.route.data .subscribe((data: { mydata: myData }) => { this.id = data.mydata.id; }); } ///// 

Y en la ruta algo así (generalmente en el archivo app.routing.ts):

 //// {path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}} ////