Angular2:如何在渲染组件之前加载数据?
Posted
技术标签:
【中文标题】Angular2:如何在渲染组件之前加载数据?【英文标题】:Angular2: How to load data before rendering the component? 【发布时间】:2016-06-09 21:12:29 【问题描述】:我试图在组件被渲染之前从我的 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
。
问题:我是否使用了一种好的方法?如果不;在组件开始渲染之前加载数据的最佳方式是什么?
注意:ngOnInit
方法用于此angular2 tutorial。
编辑:
两种可能的解决方案。首先是放弃fetchEvent
,只在ngOnInit
函数中使用API服务。
ngOnInit()
this._apiService.get.event(this.eventId).then(event => this.ev = event);
第二个解决方案。喜欢给出的答案。
fetchEvent(): Promise<EventModel>
return this._apiService.get.event(this.eventId);
ngOnInit()
this.fetchEvent().then(event => this.ev = event);
【问题讨论】:
auth gaurds 也可用于此目的 为什么使用Http而不是HttpClient?如果您使用 HttpClient,则不必像自动将响应转换为 json。 【参考方案1】:更新
如果您使用路由器,您可以使用生命周期挂钩或解析器来延迟导航,直到数据到达。 https://angular.io/guide/router#milestone-5-route-guards
在根组件初始渲染之前加载数据APP_INITIALIZER
可以使用How to pass parameters rendered from backend to angular2 bootstrap method
原创
当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;
【讨论】:
实际上,您的第一条评论效果很好。我只删除了整个fetchEvent
函数,只是将apiService.get.event
函数放在ngOnInit
中,它按我的预期工作。谢谢!我也会尽快接受你的回答。
出于某种原因,我在 Angular 1.x 上使用了这种方法。恕我直言,这应该被视为一种技巧,而不是一个适当的解决方案。我们应该在 Angular 5 上做得更好。
你到底有什么不喜欢的?我认为这两种方法都很好。【参考方案2】:
您可以在 Angular2+ 中使用解析器预取您的数据,解析器会在您的组件完全加载之前处理您的数据。
在很多情况下,您希望仅在发生某些事情时才加载组件,例如,仅当用户已经登录时才导航到仪表板,在这种情况下,解析器非常方便。
查看我为您创建的简单图表,了解您可以使用解析器将数据发送到组件的一种方式。
将 Resolver 应用到您的代码非常简单,我为您创建了 sn-ps 以了解如何创建 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;
);
/////
在 Route 中是这样的(通常在 app.routing.ts 文件中):
////
path: 'yourpath/:id', component: YourComponent, resolve: myData: MyResolver
////
【讨论】:
我想你错过了Routes
数组下的路由配置条目(通常在app.routing.ts文件中):path: 'yourpath/:id', component: YourComponent, resolve: myData: MyData
@Voicu,它不应该涵盖所有部分,但我同意,它使答案更全面,所以我补充说......谢谢
只是为了更正最后一部分,路由中的resolve参数需要指向resolver本身,而不是数据类型,即resolve: myData: MyResolver
MyData 是在 MyService 中定义的模型对象吗?
此解决方案适用于在页面加载之前获取数据,但不适用于检查登录用户或用户角色。为此,您应该使用 Guards【参考方案3】:
我发现的一个很好的解决方案是在 UI 上执行以下操作:
<div *ngIf="isDataLoaded">
...Your page...
</div
仅当:isDataLoaded 为 true 时才会呈现页面。
【讨论】:
我认为这个解决方案只适用于 Angular 1 而不是 Angular >= 2,因为 Angular 的单向数据流规则禁止在视图组合后对其进行更新。这两个钩子都会在组件的视图合成后触发。 div 根本没有被渲染。我们不打算阻止它渲染,我们想延迟它。它不起作用的原因是因为angular2中没有两种方式绑定。 @phil 你能解释一下它是如何为你工作的吗? 好的,我使用的是ChangeDetectionStrategy.Push
,将其更改为ChangeDetectionStrategy.Default
使其与模板进行双向绑定。
角度 6. 工作。以上是关于Angular2:如何在渲染组件之前加载数据?的主要内容,如果未能解决你的问题,请参考以下文章
Angular 2 - 如何在等待 http 请求完成时阻止 GUI 渲染
如何在 Vue 组件渲染之前加载 Axios Response?