等待Angular 2在渲染视图/模板之前加载/parsing模型

在Angular 1.x中,UI-Router是我的主要工具。 通过返回“parsing”值的承诺,路由器将简单地等待承诺在渲染指令之前完成。

或者,在Angular 1.x中,null对象不会使模板崩溃,所以如果我不介意临时不完整的渲染,我可以使用$digestpromise.then()后面渲染一个最初为空的模型对象。

在这两种方法中,如果可能的话,我宁愿等待加载视图,如果资源无法加载,则取消path导航。 这节省了我的“导航”的工作。 编辑:请注意这个具体意味着这个问题要求一个Angular 2期货兼容或最佳实践的方法来做到这一点,并要求尽可能避免“猫王操作员”! 因此,我没有select这个答案。

但是,这两种方法都不适用于Angular 2.0。 当然有一个标准的解决scheme或可用于此。 有人知道它是什么吗?

 @Component() { template: '{{cats.captchans.funniest}}' } export class CatsComponent { public cats: CatsModel; ngOnInit () { this._http.get('/api/v1/cats').subscribe(response => cats = response.json()); } } 

下面的问题可能会反映相同的问题: 在加载数据的PROMISE之后Angular 2呈现模板 。 请注意,问题中没有代码或接受的答案。

@angular/router具有路由的“ Resolve属性。 因此,您可以在呈现路由视图之前轻松parsing数据。

请参阅: https : //angular.io/docs/ts/latest/api/router/index/Resolve-interface.html

从截至今天,2017年8月28日的文档示例:

 class Backend { fetchTeam(id: string) { return 'someTeam'; } } @Injectable() class TeamResolver implements Resolve<Team> { constructor(private backend: Backend) {} resolve( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<any>|Promise<any>|any { return this.backend.fetchTeam(route.params.id); } } @NgModule({ imports: [ RouterModule.forRoot([ { path: 'team/:id', component: TeamCmp, resolve: { team: TeamResolver } } ]) ], providers: [TeamResolver] }) class AppModule {} 

现在,您的路线将不会被激活,直到数据被parsing并返回。

在组件中访问parsing的数据

要在运行时从组件中访问已parsing的数据,有两种方法。 所以根据你的需要,你可以使用:

  1. 返回一个string的route.snapshot.paramMap ,或者
  2. route.paramMap返回一个Observable你可以.subscribe()

例:

  // the no-observable method this.dataYouResolved= this.route.snapshot.paramMap.get('id'); // console.debug(this.licenseNumber); // or the observable method this.route.paramMap .subscribe((params: ParamMap) => { // console.log(params); this.dataYouResolved= params.get('id'); return params.get('dataYouResolved'); // return null }); console.debug(this.dataYouResolved); 

我希望有帮助。

尝试{{model?.person.name}}这应该等待模型不被undefined ,然后渲染。

Angular 2是指这个?. 语法作为猫王操作符 。 在文档中引用它很难find所以这里是它的副本,以防他们改变/移动它:

Elvis运算符(?。)和null属性path

Angular“Elvis”运算符(?。)是一种防止属性path中的null和undefined值的stream畅方便的方法。 在这里,如果currentHero为空,则防止视图呈现失败。

The current hero's name is {{currentHero?.firstName}}

让我们来详细说明这个问题和这个特定的解决scheme。

当以下数据绑定标题属性为空时会发生什么?

The title is {{ title }}

视图仍然呈现,但显示的值是空白的; 我们只看到“标题是”后面没有任何东西。 这是合理的行为。 至less该应用程序不会崩溃。

假设模板expression式包含一个属性path,就像下面这个例子显示的是一个空的英雄的firstName。

The null hero's name is {{nullHero.firstName}}

JavaScript会引发一个空引用错误,Angular也会引发这个错误:

TypeError: Cannot read property 'firstName' of null in [null]

更糟的是,整个视图消失。

如果我们相信英雄属性不能为空,我们可以声称这是合理的行为。 如果它永远不能为空,但它是空的,我们已经犯了一个编程错误,应该被捕获和修复。 抛出exception是正确的。

另一方面,属性path中的空值可能会不时确定,特别是当我们知道数据将最终到达时。

当我们等待数据时,视图应该没有怨言地呈现,而null属性path应该像title属性一样显示为空白。

不幸的是,当currentHero为null时,我们的应用程序崩溃。

我们可以用NgIf来解决这个问题

<!--No hero, div not displayed, no error --> <div *ngIf="nullHero">The null hero's name is {{nullHero.firstName}}</div>

或者我们可以尝试用&&来链接属性path的一部分,因为知道expression式在遇到第一个null的时候就会放弃。

The null hero's name is {{nullHero && nullHero.firstName}}

这些方法具有优点,但是可能会很麻烦,特别是如果财产path很长。 想象一下,在诸如abcd之类的很长的属性path中的某个地方,可以防止出现null

angular度“猫王”运算符(?)是一种更为stream畅和方便的方法来防止在物业path上的空值。 expression式在达到第一个空值时会被释放。 显示器是空白的,但应用程序保持滚动,没有错误。

<!-- No hero, no problem! --> <!-- No hero, no problem! --> The null hero's name is {{nullHero?.firstName}}

它也适用于很长的属性path:

a?.b?.c?.d

编辑:有angular度的团队已经发布了@Resolve装饰器。 它仍然需要一些澄清,如何工作,但在此之前,我会采取别人的相关答案,并提供其他来源的链接:

  • StackOverflow:在Angular2路线中使用parsing
  • StackOverflow:GünterZöchbauer的使用示例
  • 解决文档
  • GitHub上的路由器补丁
  • RC4 ChangeLog
  • RC4发布鸣叫

编辑:这个答案只适用于Angular 2testing版。 截至此编辑,路由器并未发布用于Angular 2 RC。 相反,在使用Angular 2 RC时,请将router引用replace为router-deprecated router ,以继续使用Beta路由器。

未来的Angular2实现方式将通过@Resolve装饰器。 在此之前,每个Brandon Roberts最近的传真是CanActivate组件装饰器。 请参阅https://github.com/angular/angular/issues/6611

尽pipebeta 0不支持向组件提供已解决的值,但是已经计划好了,还有一个解决方法如下所述: 在Angular2中使用Resolve

testing版1的例子可以在这里find: http : //run.plnkr.co/BAqA98lphi4rQZAd/#/resolved 。 它使用非常类似的解决方法,但稍微更准确地使用RouteData对象而不是RouteParams

 @CanActivate((to) => { return new Promise((resolve) => { to.routeData.data.user = { name: 'John' } 

此外,请注意,还有一个示例解决方法可用于访问嵌套/父路由“已解决”的值,以及您期望使用1.x UI-Router的其他function。

请注意,您还需要手动注入所需的任何服务来完成此操作,因为Angular Injector层次结构在CanActivate装饰器中当前不可用。 只需导入注入器将创build一个新的注入器实例,而无需从bootstrap()访问提供者,因此您可能需要存储引导程序注入器的应用程序范围的副本。 布兰登在这个页面上的第二个Plunk链接是一个很好的起点: https : //github.com/angular/angular/issues/4112

用观察者设置一个本地值

…另外,不要忘记用虚拟数据初始化值,以避免uninitialized错误。

 export class ModelService { constructor() { this.mode = new Model(); this._http.get('/api/v1/cats') .map(res => res.json()) .subscribe( json => { this.model = new Model(json); }, error => console.log(error); ); } } 

这里假设Model是一个表示数据结构的数据模型。

不带参数的模型应创build一个初始化(但为空)的所有值的新实例。 这样,如果模板在数据接收之前呈现,它不会抛出错误。

理想情况下,如果你想持久化数据以避免不必要的http请求,你应该把它放在一个拥有你自己的观察者的对象中,你可以订阅它。

在您的@Component实现routerOnActivate并返回您的承诺:

https://angular.io/docs/ts/latest/api/router/OnActivate-interface.html

编辑:这显然不起作用,虽然目前的文件可能有点难以解释这个话题。 有关更多信息,请参阅Brandon的第一条评论: https : //github.com/angular/angular/issues/6611

编辑:否则通常准确的Auth0网站上的相关信息是不正确的: https ://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in- 深度/

编辑:有angular的团队正在为此计划一个@Resolve装饰器。

我发现一个很好的解决scheme就是在UI上做这样的事情:

 <div *ngIf="vendorServicePricing && quantityPricing && service"> ...Your page... </div 

只有在vendorServicePricingquantityPricingservice被加载的时候才会呈现页面。

一个简单的解决办法是在init / constructor中初始化对象的属性

cats.captchans.funniest

一旦承诺解决了,它将绑定实际值。