Angular2: ¿Cómo cargar datos antes de renderizar el componente?
Estoy intentando cargar un evento desde mi API antes de que el componente se procese. Actualmente estoy usando mi servicio API 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
plantilla
<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 que la llamada a la API en la función ngOnInit
termine de obtener los datos. Así que nunca puedo ver el id de evento en mi plantilla de vista. Así que parece que esto es una ASÍNCRONA problema. Esperaba la unión de la ev
(EventRegister
component) para hacer algún trabajo después de establecer la propiedad ev
. Lamentablemente no muestra el div
marcado con *ngIf="ev"
cuando se establece la propiedad.
Pregunta: ¿Estoy usando un buen enfoque? Si no, ¿Cuál es la mejor manera de cargar datos antes de que el componente comience a renderizarse?
NOTA: El enfoque ngOnInit
se utiliza en este tutorial angular2.
EDITAR:
Dos posibles soluciones. Primero fue deshacerse de fetchEvent
y simplemente usar el servicio API en la función ngOnInit
.
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);
}
3 answers
Update
Si usa el enrutador, puede usar hooks o resolutores de ciclo de vida para retrasar la navegación hasta que lleguen los datos. https://angular.io/guide/router#milestone-5-route-guards
Para cargar datos antes del renderizado inicial del componente raíz
APP_INITIALIZER
se puede usar Cómo pasar parámetros renderizados desde el backend al método bootstrap angular2
Original
Cuando console.log(this.ev)
es ejecutado después de this.fetchEvent();
, esto no significa que la llamada fetchEvent()
esté hecha, esto solo significa que está programada. Cuando se ejecuta console.log(this.ev)
, la llamada al servidor ni siquiera se realiza y, por supuesto, todavía no ha devuelto un valor.
Cambie 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
});
}
Cambiar ngOnInit()
a esperar a que el Promise
para completar
ngOnInit() {
this.fetchEvent().then(() =>
console.log(this.ev)); // Now has value;
}
Esto en realidad no le comprará mucho para su caso de uso.
Mi sugerencia: Envuelve toda tu 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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-15 22:01:56
Una buena solución que he encontrado es hacer en la interfaz de usuario algo como:
<div *ngIf="isDataLoaded">
...Your page...
</div
Solo cuando: isDataLoaded es true la página es renderizada.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-26 09:31:02
Puede pre-obtener sus datos mediante el uso de Resolutores en Angular2+, los resolutores procesan sus datos antes de que su Componente se cargue completamente.
Hay muchos casos en los que desea cargar su componente solo si hay cierta cosa sucediendo, por ejemplo, navegue al Panel de control solo si la persona ya ha iniciado sesión, en este caso los resolutores son muy útiles.
Mire el diagrama simple que creé para usted para una de las formas en que puede usar el solucionador para enviar los datos a tu componente.
Aplicar Resolver a tu código es bastante simple, he creado 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 así:
/////
ngOnInit() {
this.route.data
.subscribe((data: { mydata: myData }) => {
this.id = data.mydata.id;
});
}
/////
Y en la Ruta algo como esto (generalmente en la aplicación.enrutamiento.archivo ts):
////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
////
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-19 16:15:23