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);
}
3 réponses
mise à jour
-
si vous utilisez le routeur, vous pouvez utiliser des crochets lifecycle ou des résolveurs pour retarder la navigation jusqu'à l'arrivée des données. https://angular.io/guide/router#milestone-5-route-guards
-
pour charger les données avant le rendu initial du composant racine
APP_INITIALIZER
peut être utilisé Comment passer les paramètres rendus à partir backend à angular2 la méthode du bootstrap
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;
}
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.
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.
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}}
////