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);
}
mettre à jour
Si vous utilisez le routeur, vous pouvez utiliser des hooks ou des résolveurs de cycle de vie pour retarder la navigation jusqu'à ce que les données soient arrivées. https://angular.io/guide/router#milestone-5-route-guards
Pour charger des données avant le rendu initial du composant racine, vous pouvez utiliser la méthode APP_INITIALIZER
Comment passer des paramètres du système de gestion à la méthode angular2 bootstrap
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;
}
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.
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.
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}}
////