Angular2: как загрузить данные перед отрисовкой компонента?
я пытаюсь загрузить событие из моего API до того, как компонент будет отображен. В настоящее время я использую свой сервис API, который я вызываю из функции ngOnInit компонента.
мой 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
}
}
мой 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>
мой сервис 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();
}
}
}
компонент отображается перед вызовом API в ngOnInit
функция выполнена для извлечения данных. Поэтому я никогда не вижу идентификатор события в своем представлении шаблон. Похоже, это асинхронная проблема. Я ожидал связывания ev
(EventRegister
компонент), чтобы сделать некоторую работу после ev
свойство установлено. К сожалению, он не показывает div
С пометкой *ngIf="ev"
когда свойство будет установлено.
вопрос: я использую хороший подход? Если нет; каков наилучший способ загрузки данных до начала рендеринга компонента?
Примечание: на ngOnInit
подход используется в данной angular2 учебник.
EDIT:
два возможных решения. Сначала нужно было бросить fetchEvent
и просто используйте службу API в
3 ответов
обновление
если вы используете маршрутизатор, вы можете использовать крючки жизненного цикла или резольверы для задержки навигации до прибытия данных. https://angular.io/guide/router#milestone-5-route-guards
для загрузки данных перед первоначальной отрисовкой корневого компонента
APP_INITIALIZER
можно использовать как передать параметры, отображаемые из backend в Angular2 bootstrap метод
оригинал
, когда console.log(this.ev)
выполняется после this.fetchEvent();
, это не значит fetchEvent()
вызов выполнен, это означает только, что он запланирован. Когда console.log(this.ev)
выполняется, вызов на сервер даже не сделан и, конечно, еще не вернул значение.
изменить fetchEvent()
возвратить 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
});
}
изменить ngOnInit()
ждать Promise
в полный
ngOnInit() {
this.fetchEvent().then(() =>
console.log(this.ev)); // Now has value;
}
это на самом деле не покупать вам много для вашего случая.
мое предложение: оберните весь шаблон в <div *ngIf="isDataAvailable"> (template content) </div>
и ngOnInit()
isDataAvailable:boolean = false;
ngOnInit() {
this.fetchEvent().then(() =>
this.isDataAvailable = true); // Now has value;
}
хорошее решение, которое я нашел-это сделать на UI что-то вроде:
<div *ngIf="isDataLoaded">
...Your page...
</div
только когда: isDataLoaded верно отображается страница.
вы можете предварительная загрузка ваши данные с помощью распознавателей в Angular2+, распознаватели обрабатывают данные до полной загрузки компонента.
есть много случаев, когда вы хотите загрузить свой компонент только в том случае, если происходит определенная вещь, например, перейдите к панели мониторинга, только если человек уже вошел в систему, в этом случае решатели так удобны.
посмотрите на простую диаграмму, которую я создал для вас для одного из способов использования 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;
}
});
}
}
и модуль:
import { MyResolver } from './my-resolver.service';
@NgModule({
imports: [
RouterModule.forChild(myRoutes)
],
exports: [
RouterModule
],
providers: [
MyResolver
]
})
export class MyModule { }
и вы можете получить доступ к нему в своем компонент такой:
/////
ngOnInit() {
this.route.data
.subscribe((data: { mydata: myData }) => {
this.id = data.mydata.id;
});
}
/////
и маршрут что-то вроде этого (обычно в приложение.маршрутизирующий.файл ТС):
////
{path: 'yourpath/:id', component: YourComponent, resolve: { myData: MyData}}
////