为什么 Angular 2 的模板不从 angular 区域外的调用更新?

Why is Angular 2's template not updating from calls outside an angular zone?

我想我会创建一个非常简单的登录表单,其中包含 component-bound 用户名和密码属性,可以 运行 通过以下步骤:

  1. 使用 fetch() 调用提交凭据
  2. 然后获取响应结果object的JSON内容
  3. THEN 检查 server-side 检查结果的内容

如果凭据错误,它会更改一个组件 属性,告诉 Angular 将 class 应用于用户名和密码输入,使它们暂时变为红色(使用setTimeout 将其改回)

我 运行 遇到的问题是 Angular 无法正确应用 class,我不确定为什么。我决定创建一个简化的测试项目来缩小问题范围,并最终将 system-polyfills/when.js 包含在内。

此代码经过 1、2、3,并在组件 属性 中设置它们并将其输出到调试控制台。 Angular 正确呈现组件 属性 除非包含 system-polyfill,在这种情况下它将停止在 1 并且永远不会将其更改为 2 或 3,即使控制台显示 属性 实际上改变了:

export class App {
    private stateIndicator:string = "0";

    public showState(state:string) {
        console.log('State: ' + state);
        this.stateIndicator = state;

    }

    public showFetchProblem() {
        this.showState('1')
        fetch('http://www.google.com/robots.txt',{mode:'no-cors'}).then((response:any) => {
            this.showState('2')
            response.text().then((value) => {
                this.showState('3')
            });
        });
    }
}

我创建了以下 Plunker 来演示此行为:http://plnkr.co/edit/GR9U9fTctmkSGsPTySAI?p=preview

是的,显而易见的解决方案是:

  1. 不要手动包含 system-polyfill 或
  2. 如果需要,在 SystemJS 之前手动包含一个不同的 Promise polyfill

但我仍然很好奇为什么会这样,希望有人能对此有所说明(并可能有助于解决基本问题)。

编辑: 原标题是Why is Angular 2's template rendering misbehaving when using system-polyfills (when.js)。感谢 Thierry 和 Günter 的贡献并指出 Angular 2 对区域的使用在这里发挥了作用。对于以后遇到这种情况的任何人,这里有两篇优秀的文章,它们更详细地解释了区域,如果您 运行 参与其中,将会增强您对这种情况的理解:

  1. http://blog.thoughtram.io/angular/2016/01/22/understanding-zones.html
  2. http://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html

更新

不再确定解释,但解决方法有效。但我认为还有其他一些潜在问题。

另见 https://github.com/angular/angular/issues/7792#issuecomment-211217775

原创

我假设问题是由于 fetch 没有被 Angulars 区域修补

To work around make the code execute inside Angulars zone manually

export class App {
    constructor(private zone:NgZone) {}

    private stateIndicator:string = "0";

    public showState(state:string) {
        console.log('State: ' + state);
        this.stateIndicator = state;

    }

    public showFetchProblem() {
        this.showState('1')
        fetch('http://www.google.com/robots.txt',{mode:'no-cors'}).then((response:any) => {
            this.showState('2')
            response.text().then((value) => {
                this.zone.run(() => this.showState('3'));
            });
        });
    }
}

Plunker example

Promise polyfill 提供了 Promise 的自定义实现,它似乎在 Angular2 的范围之外执行(即不在区域中)。

如果您在 Angular2 管理的区域中显式执行您的代码(使用 NgZone class),它会在包含 system-polyfill.js 文件时工作。

这是一个示例:

constructor(private zone:NgZone) {}

public showFetchProblem() {
  this.showState('1')
  this.zone.run(() => {
    fetch('http://www.google.com/robots.txt',{mode:'no-cors'}).then((response:any) => {
      this.showState('2')
      response.text().then((value) => {
        this.showState('3')
      });
    });
  });
}

查看对应的plunkr: http://plnkr.co/edit/EJgZKWVx6FURrelMEzN0?p=preview.