Pregunta 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 EventRegister componente:

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 EventRegister modelo

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

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

El componente se procesa antes de la llamada API en el ngOnInit la función se realiza buscando 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 de la ev (EventRegister componente) para hacer algún trabajo después de la ev propiedad fue establecida. Lamentablemente, no muestra el div marcado con *ngIf="ev" cuando la propiedad se establece.

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: los ngOnInit enfoque se utiliza en este tutorial angular2.

EDITAR:

Dos posibles soluciones Primero fue abandonar el fetchEvent y solo usa el servicio API en el ngOnInit función.

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

Segunda solución Como la respuesta dada.

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

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

76
2018-02-26 15:17


origen


Respuestas:


actualizar

original

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

Cambio fetchEvent() para devolver un 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
    });
 }

cambio ngOnInit() esperar por el Promise completar

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 toda su plantilla en un <div *ngIf="isDataAvailable"> (template content) </div>

y en ngOnInit()

isDataAvailable:boolean = false;

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

82
2018-02-26 15:22



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

<div *ngIf="isDataLoaded">
 ...Your page...
</div

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


24
2017-07-26 09:31



Usted puede precarga sus datos mediante el uso de 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 diagrama 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

Aplicando Resolver para 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<MyData> {
  constructor(private ms: MyService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<MyData> {
    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 puedes acceder a él en tu Componente Me gusta esto:

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

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

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

21
2018-06-08 14:00