在渲染之前等待 Angular 2 到 load/resolve 模型 view/template

Wait for Angular 2 to load/resolve model before rendering view/template

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

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

在这两种方法中,如果可能的话我宁愿等待加载视图,如果无法加载资源则取消路由导航。这为我节省了 "un-navigating" 的工作。 编辑:请注意,这具体意味着此问题需要 Angular 2 期货兼容或最佳实践方法来执行此操作,并要求尽可能避免 "Elvis operator"!因此,我没有select那个答案。

然而,这两种方法在 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。请注意,该问题中没有代码或可接受的答案。

在您的@Component和return中实现您的routerOnActivate承诺:

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

编辑:这显然不起作用,尽管当前文档在这个主题上可能有点难以解释。有关更多信息,请参阅布兰登的第一条评论:https://github.com/angular/angular/issues/6611

编辑:otherwise-usually-accurate Auth0 站点上的相关信息不正确:https://auth0.com/blog/2016/01/25/angular-2-series-part-4-component-router-in-depth/

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

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

Angular 2 将此 ?. 语法称为 Elvis operator。在文档中很难找到对它的引用,所以这里有一份它的副本,以防他们 change/move 它:

The Elvis Operator ( ?. ) and null property paths

The Angular “Elvis” operator ( ?. ) is a fluent and convenient way to guard against null and undefined values in property paths. Here it is, protecting against a view render failure if the currentHero is null.

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

Let’s elaborate on the problem and this particular solution.

What happens when the following data bound title property is null?

The title is {{ title }}

The view still renders but the displayed value is blank; we see only "The title is" with nothing after it. That is reasonable behavior. At least the app doesn't crash.

Suppose the template expression involves a property path as in this next example where we’re displaying the firstName of a null hero.

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

JavaScript throws a null reference error and so does Angular:

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

Worse, the entire view disappears.

We could claim that this is reasonable behavior if we believed that the hero property must never be null. If it must never be null and yet it is null, we've made a programming error that should be caught and fixed. Throwing an exception is the right thing to do.

On the other hand, null values in the property path may be OK from time to time, especially when we know the data will arrive eventually.

While we wait for data, the view should render without complaint and the null property path should display as blank just as the title property does.

Unfortunately, our app crashes when the currentHero is null.

We could code around that problem with NgIf

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

Or we could try to chain parts of the property path with &&, knowing that the expression bails out when it encounters the first null.

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

These approaches have merit but they can be cumbersome, especially if the property path is long. Imagine guarding against a null somewhere in a long property path such as a.b.c.d.

The Angular “Elvis” operator ( ?. ) is a more fluent and convenient way to guard against nulls in property paths. The expression bails out when it hits the first null value. The display is blank but the app keeps rolling and there are no errors.

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

It works perfectly with long property paths too:

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);
      );
    }
}

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

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

理想情况下,如果您想保留数据以避免不必要的 http 请求,您应该将其放在一个对象中,该对象具有您可以订阅的自己的观察者。

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


编辑:此答案仅适用于 Angular 2 BETA。截至本次编辑,路由器未针对 Angular 2 RC 发布。相反,当使用 Angular 2 RC 时,将对 router 的引用替换为 router-deprecated 以继续使用 beta 路由器。

Angular2 未来的实现方式将通过 @Resolve 装饰器实现。在那之前,最接近的传真是 CanActivate Component decorator,根据 Brandon Roberts 的说法。见 https://github.com/angular/angular/issues/6611

尽管 beta 0 不支持向组件提供已解析的值,但它已在计划中,并且此处还描述了一种解决方法:

可在此处找到 beta 1 示例:http://run.plnkr.co/BAqA98lphi4rQZAd/#/resolved。它使用非常相似的解决方法,但使用 RouteData 对象而不是 RouteParams.

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

此外,请注意,还有一个示例解决方法用于访问 nested/parent 路由 "resolved" 值,以及您使用 1.x [=60 时期望的其他功能=].

请注意,您还需要手动注入完成此操作所需的任何服务,因为 Angular 注入器层次结构目前在 CanActivate 装饰器中不可用。简单地导入一个注入器将创建一个新的注入器实例,无法访问来自 bootstrap() 的提供程序,因此您可能想要存储一个 application-wide 自举注入器的副本。 Brandon 在此页面上的第二个 Plunk link 是一个很好的起点:https://github.com/angular/angular/issues/4112

@angular/routerResolve 属性 路线。因此,您可以在渲染路线视图之前轻松解析数据。

参见: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其中returns一个字符串,或者
  2. route.paramMap 您可以 returns 一个 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);

希望对您有所帮助。

我发现的一个很好的解决方案是在 UI 上做类似的事情:

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

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