75

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

或者,在 Angular 1.x 中,空对象不会使模板崩溃 - 因此,如果我不介意暂时不完整的渲染,我可以在填充最初为空的模型对象$digest后使用来渲染。promise.then()

在这两种方法中,如果可能的话,我宁愿等待加载视图,如果无法加载资源,则取消路线导航。这为我节省了“取消导航”的工作。编辑:请注意,这特别意味着此问题要求使用 Angular 2 期货兼容或最佳实践方法来执行此操作,并要求尽可能避免使用“猫王操作员”!因此,我没有选择那个答案。

然而,这两种方法在 Angular 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());
    }
}

以下问题可能反映了同样的问题:Angular 2 render template after the PROMISE with data is loaded。请注意,问题中没有代码或可接受的答案。

4

6 回答 6

86

试试{{model?.person.name}}这个应该等待模型不存在undefined然后渲染。

Angular 2 将此?.语法称为Elvis 运算符。很难在文档中找到对它的引用,因此这里是它的副本,以防他们更改/移动它:

Elvis 运算符 ( ?. ) 和 null 属性路径

Angular “Elvis”运算符 (?.) 是一种流畅且方便的方法,可以防止属性路径中出现空值和未定义值。就是这样,如果 currentHero 为空,则防止视图渲染失败。

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

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

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

The title is {{ title }}

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

假设模板表达式包含一个属性路径,就像在下一个示例中我们要显示一个空英雄的名字一样。

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

JavaScript 会抛出空引用错误,Angular 也是如此:

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

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

如果我们认为 hero 属性绝不能为空,我们可以声称这是合理的行为。如果它一定不能为空,但它仍然为空,我们就犯了一个应该被捕获和修复的编程错误。抛出异常是正确的做法。

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

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

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

我们可以用 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}}

这些方法有优点,但它们可能很麻烦,尤其是在财产路径很长的情况下。想象一下,在诸如 abcd 之类的长属性路径中的某处防止出现空值

Angular 的“Elvis”运算符 (?.) 是一种更流畅、更方便的方法来防止属性路径中出现空值。表达式在遇到第一个空值时退出。显示为空白,但应用程序不断滚动并且没有错误。

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

它也适用于长属性路径:

a?.b?.c?.d

于 2016-01-12T07:48:25.647 回答
38

该包@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. 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);

我希望这会有所帮助。

于 2016-07-05T21:01:27.133 回答
7

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


编辑:此答案仅适用于 Angular 2 BETA。在本次编辑中,没有为 Angular 2 RC 发布路由器。相反,在使用 Angular 2 RC 时,将引用替换为routerrouter-deprecated继续使用 beta 路由器。

Angular2 未来的实现方式将是通过 @Resolve 装饰器。在那之前,最接近的传真是CanActivateComponent decorator,根据 Brandon Roberts。见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

于 2016-01-21T14:59:22.580 回答
4

使用观察者设置局部值

...另外,不要忘记使用虚拟数据初始化值以避免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);
      );
    }
}

这假定模型是表示数据结构的数据模型。

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

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

于 2016-01-13T21:27:20.380 回答
3

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

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

只有当: vendorServicePricing,quantityPricingservice被加载时,页面才会被渲染。

于 2017-06-22T18:31:39.993 回答
2

实现routerOnActivate你的@Component并回报你的承诺:

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 装饰器。

于 2016-01-11T22:13:31.350 回答