web-dev-qa-db-fra.com

Angular2: Comment charger des données avant de rendre le composant?

J'essaie de charger un événement à partir de mon API avant que le composant ne soit rendu. J'utilise actuellement mon service API que j'appelle à partir de la fonction ngOnInit du composant.

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

Mon modèle EventRegister

<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 que l'appel d'API dans la fonction ngOnInit soit terminé pour récupérer les données. Je ne vois donc jamais l'identifiant de l'événement dans mon modèle de vue. Il semble donc que ce soit un problème ASYNC. Je m'attendais à ce que la liaison du composant ev (EventRegister) effectue certains travaux après la définition de la propriété ev. Malheureusement, il ne montre pas le div marqué par *ngIf="ev" lorsque la propriété est définie.

Question: Est-ce que j'utilise une bonne approche? Si non; Quel est le meilleur moyen de charger des données avant que le rendu du composant commence?

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

MODIFIER:

Deux solutions possibles. La première consistait à abandonner la fetchEvent et à utiliser simplement le service API dans la fonction ngOnInit.

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

Deuxième solution. J'aime la réponse donnée.

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

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

mettre à jour

original

Lorsque console.log(this.ev) est exécuté après this.fetchEvent();, cela ne signifie pas que l'appel fetchEvent() est terminé, cela signifie simplement qu'il est planifié. Lorsque console.log(this.ev) est exécuté, l'appel du serveur n'est même pas effectué et, bien entendu, il n'a pas encore renvoyé de valeur.

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

changez ngOnInit() pour attendre que Promise se termine

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

En fait, cela ne vous achètera pas beaucoup pour votre cas d'utilisation.

Ma suggestion: envelopper tout votre gabarit dans une <div *ngIf="isDataAvailable"> (template content) </div>

et dans ngOnInit()

isDataAvailable:boolean = false;

ngOnInit() {
    this.fetchEvent().then(() =>
    this.isDataAvailable = true); // Now has value;
}
110
Günter Zöchbauer

Une solution intéressante que j'ai trouvée consiste à faire sur l'interface utilisateur quelque chose comme:

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

Seulement lorsque: isDataLoaded a la valeur true, la page est rendue.

44
user2965814

Vous pouvez pré-extraire vos données à l'aide de Resolvers dans Angular2 + , Resolvers traiter vos données avant que votre composant ne soit complètement chargé.

Dans de nombreux cas, vous ne souhaitez charger votre composant que s'il se produit un événement. Par exemple, accédez à Tableau de bord uniquement si la personne est déjà connectée. Dans ce cas, les résolveurs sont très pratiques.

Examinez le diagramme simple que j'ai créé pour vous et utilisez l'une des manières d'utiliser le résolveur pour envoyer les données à votre composant.

enter image description here

Appliquer Resolver à votre code est assez simple, j'ai créé les extraits pour vous permettre 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 dans votre composant comme ceci:

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

Et dans la Route , quelque chose comme ceci (généralement dans le fichier app.routing.ts):

////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyResolver}}
////
37
Alireza