在渲染视图/模板之前等待角度2加载/解析模型

在 Angular 1.x 中,用户界面路由器是我的主要工具。通过返回“解析”值的承诺,路由器只需等待承诺完成后再呈现指令。

另外,在 Angular 1.x 中,一个 null 对象不会使模板崩溃——所以如果我不介意一个暂时不完整的渲染,我可以在 promise.then()填充一个初始的空模型对象之后使用 $digest来渲染。

在这两种方法中,如果可能的话,我希望等待加载视图,如果无法加载资源,则取消路由导航。这让我省去了“解除导航”的工作。编辑: 注意,这特别意味着这个问题要求一个角2期货兼容或最佳实践的方法来做到这一点,并要求避免“猫王运算符”,如果可能的话!因此,我没有选择那个答案。

然而,这两种方法都不适用于角度2.0。当然有一个标准的解决方案计划或可用于这一点。有人知道这是什么吗?

@Component() {
template: '{{cats.captchans.funniest}}'
}
export class CatsComponent {


public cats: CatsModel;


ngOnInit () {
this._http.get('/api/v1/cats').subscribe(response => cats = response.json());
}
}

下面的问题可能反映了同样的问题: 角度2渲染模板后的 PROMISE 与数据加载。请注意,这个问题没有代码,也没有公认的答案。

103291 次浏览

在你的 @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-depth/

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

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

角度2将此 ?.语法称为 猫王接线员。在文档中很难找到对它的参考,所以这里有一份副本,以防他们更改或移动它:

Elvis 操作符(? .)和 null 属性路径

角形的“猫王”操作员(?. )是在属性路径中防止空值和未定义值的一种流畅和方便的方法。如下所示,如果 currentHero 为 null,则防止视图呈现失败。

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

让我们详细说明这个问题和这个特殊的解决方案。

当下面的数据绑定 title 属性为 null 时会发生什么?

The title is \{\{ title }}

视图仍然呈现,但显示的值为空; 我们只看到“ The title is”,后面没有任何内容。这是合理的行为。至少应用程序不会崩溃。

假设模板表达式包含一个属性路径,如下面的示例所示,其中我们显示了一个空主角的 firstName。

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

JavaScript 抛出一个 null 引用错误,Angular 也是如此:

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

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

我们可以声称这是合理的行为,如果我们相信英雄的财产永远不会是无效的。如果它永远不能为 null,但它仍然为 null,那么我们就犯了一个应该被捕获和修复的编程错误。引发异常是正确的做法。

另一方面,属性路径中的空值有时可能是可以的,特别是当我们知道数据最终会到达时。

当我们等待数据时,视图应该毫无怨言地呈现,并且 null 属性路径应该像 title 属性一样显示为空。

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

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

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

或者我们可以尝试使用 & & 来链接属性路径的某些部分,因为我们知道表达式在遇到第一个 null 时会退出。

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

这些方法有优点,但它们可能很麻烦,特别是在属性路径很长的情况下。想象一下在长属性路径(如 a.b.c.d)中的某个地方防范 null。

角形的“猫王”操作员(?. )是防止属性路径中出现 null 的更流畅、更方便的方法。表达式在遇到第一个空值时退出。显示是空白的,但应用程序继续滚动,没有错误。

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

它对于长属性路径也非常有效:

a?.b?.c?.d

使用观察者设置局部值

... 还有,不要忘记用虚拟数据初始化值,以避免 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 是一个表示数据结构的数据模型。

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

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

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


编辑: 这个答案只适用于角度2β。路由器不发布角度2 RC 作为这个编辑。相反,当使用角2 RC 时,用 router-deprecated替换对 router的引用,以继续使用 beta 路由器。

Angular2-将来实现这个的方法是通过@Resolve 装饰器。在此之前,最接近的传真机是 CanActivate组件装饰,由布兰登罗伯茨。见 https://github.com/angular/angular/issues/6611

虽然 beta 0不支持为组件提供解析值,但是它是有计划的,这里还描述了一个解决方案: 在 Angular2路由中使用分辨率

这里可以找到一个 beta 1的例子: http://run.plnkr.co/BAqA98lphi4rQZAd/#/resolved。它使用了一个非常类似的工作区,但是使用 RouteData对象比使用 RouteParams稍微准确一些。

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

另外,请注意,还有一个用于访问嵌套/父路由“解析”值的解决方案示例,以及如果使用1.x UI-Router 的话所期望的其他特性。

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

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

见: 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 {}

现在,在解析并返回数据之前,不会激活路由。

访问组件中的已解析数据

要在运行时从组件内访问已解析的数据,有两种方法。因此,根据您的需要,您可以使用:

  1. 返回字符串的 route.snapshot.paramMap
  2. 返回一个可观察值,你可以 .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);

希望能帮上忙。

我发现了一个不错的解决方案,就是在 UI 上进行以下操作:

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

只有在加载 vendorServicePricingquantityPricingservice时,页面才会呈现。