Angular2:如何在渲染组件之前加载数据?
我正试图从我的API加载一个事件之前组件呈现。 目前,我正在使用我从组件的ngOnInit函数调用的API服务。
我的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(); } } }
在ngOnInit
函数的API调用完成获取数据之前,组件被渲染。 所以我从来没有看到我的视图模板中的事件ID。 所以看起来这是一个ASYNC问题。 我期望ev
( EventRegister
组件)的绑定在ev
属性设置之后做一些工作。 可悲的是,当属性被设置时,它不显示用*ngIf="ev"
标记的div
。
问题:我使用了一个好的方法吗? 如果不; 在组件开始呈现之前加载数据的最佳方式是什么?
注意:在这个angular2教程中使用ngOnInit
方法。
编辑:
两种可能的解决 首先是fetchEvent
并在ngOnInit
函数中使用API服务。
ngOnInit() { this._apiService.get.event(this.eventId).then(event => this.ev = event); }
第二种scheme 就像给出的答案一样。
fetchEvent(): Promise<EventModel> { return this._apiService.get.event(this.eventId); } ngOnInit() { this.fetchEvent().then(event => this.ev = event); }
更新
-
如果您使用路由器,则可以使用生命周期挂钩或parsing器来延迟导航,直到数据到达。 https://angular.io/docs/ts/latest/guide/router.html#!#guards
-
可以使用根组件
APP_INITIALIZER
的初始渲染之前加载数据如何将从后端呈现的parameter passing给angular2引导方法
原版的
当console.log(this.ev)
在this.fetchEvent();
之后执行时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; }
这实际上不会为你的用例买太多东西。
我的build议:将你的整个模板包装在一个<div *ngIf="isDataAvailable"> (template content) </div>
和ngOnInit()
isDataAvailable:boolean = false; ngOnInit() { this.fetchEvent().then(() => this.isDataAvailable = true); // Now has value; }
我发现一个很好的解决scheme就是在UI上做这样的事情:
<div *ngIf="isDataLoaded"> ...Your page... </div
只有在:isDataLoaded为true时才会呈现页面。
您可以使用Angular2 +中的Resolvers预取数据,parsing器在您的组件完全加载之前处理您的数据。
有很多情况下,只有在某些事情发生时才要加载组件,例如只有在已经login的人员的情况下才能导航到Dashboard,在这种情况下,Resolver非常方便。
看看我为您创build的简单图表,您可以使用parsing器将数据发送到组件。
将parsing器应用于您的代码非常简单,我为您创build了片段,以了解如何创buildparsing器:
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; }); } /////