Angular2: comment charger les données avant de rendre le composant?

j'essaie de charger un événement depuis mon API avant que le composant ne soit rendu. Actuellement, j'utilise mon service API que j'appelle depuis la fonction ngOnInit du composant.

Mon EventRegister composant:

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

Mon EventRegister modèle

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

mon service 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();
        }     
    }     
}

le composant est rendu avant L'appel API dans la fonction ngOnInit est fait récupérer les données. Donc je n'arrive jamais à voir l'ID de l'événement dans mon modèle de vue. On dirait que c'est un problème asynchrone. Je m'attendais à ce que la liaison du ev ( EventRegister composante) pour faire un certain travail après la propriété ev a été fixée. Malheureusement, il ne montre pas le div marqué *ngIf="ev" lorsque la propriété est défini.

Question: est-ce que j'utilise une bonne approche? Si non, Quelle est la meilleure façon de charger les données avant de les composant commence à rendre?

NOTE: l'approche ngOnInit est utilisée dans ce tutoriel angular2 .

EDIT:

deux solutions possibles. La première était de laisser tomber le fetchEvent et juste utiliser le service API dans la fonction ngOnInit .

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

deuxième solution. Comme la réponse donnée.

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

ngOnInit() {
    this.fetchEvent().then(event => this.ev = event);
}
86
demandé sur Tom Aalbers 2016-02-26 18:17:00

3 réponses

mise à jour

original

quand console.log(this.ev) est exécuté après this.fetchEvent(); , cela ne signifie pas que l'appel fetchEvent() est fait, cela signifie seulement qu'il est programmé. Lorsque console.log(this.ev) est exécutée, l'appel au serveur n'est pas encore faite et bien sûr, n'a pas encore retourner une valeur.

modifier fetchEvent() pour retourner 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
    });
 }

modifier ngOnInit() pour attendre que le Promise soit rempli

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

cela ne vous achètera pas beaucoup pour votre étui.

ma suggestion: enveloppez votre modèle entier dans un <div *ngIf="isDataAvailable"> (template content) </div>

et dans ngOnInit()

isDataAvailable:boolean = false;

ngOnInit() {
    this.fetchEvent().then(() =>
    this.isDataAvailable = true); // Now has value;
}
89
répondu Günter Zöchbauer 2017-12-15 22:01:56

une bonne solution que j'ai trouvé est de faire sur L'UI quelque chose comme:

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

Uniquement lorsque: isDataLoaded est vrai que la page est affichée.

29
répondu user2965814 2017-07-26 09:31:02

Vous pouvez pre-fetch vos données à l'aide de Résolveurs Angular2+ , Résolveurs de traiter vos données avant de votre Composant entièrement chargé.

il y a de nombreux cas où vous voulez charger votre composant uniquement s'il se passe quelque chose, par exemple naviguer sur le tableau de bord seulement si la personne déjà connectée, dans ce cas les résolveurs sont si pratiques.

regardez le diagramme simple que j'ai créé pour vous pour un de la façon dont vous pouvez utiliser cet outil pour envoyer les données à votre composant.

enter image description here

appliquer résoudre à votre code est assez simple, j'ai créé les snippets pour vous de voir comment le résolveur peut être créé:

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

et dans le module :

import { MyResolver } from './my-resolver.service';

@NgModule({
  imports: [
    RouterModule.forChild(myRoutes)
  ],
  exports: [
    RouterModule
  ],
  providers: [
    MyResolver
  ]
})
export class MyModule { }

et vous pouvez y accéder en votre composant comme ceci:

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

et dans le Route quelque chose comme ceci (habituellement dans l'application.routage.fichier ts):

////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
////
22
répondu Alireza 2018-01-19 16:15:23