javascriptangularsystemjswhen-js

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


I thought I would create a very simple login form with component-bound username and password properties that would run through the following steps:

  1. Submit credentials with a fetch() call
  2. THEN obtain the Response result object's JSON content
  3. THEN check that content for the result of the server-side check

If the credentials were wrong, it would change a component property that would tell Angular to apply a class to the username and password inputs to make them red temporarily (using setTimeout to change that back)

The problem I ran into is that Angular would not correctly apply the class, and I wasn't sure why. I decided to create a simplified test project to narrow down the problem and ended up with the inclusion of system-polyfills/when.js being cause.

This code goes through 1, 2, then 3, and sets that both in the component property and outputs it to the debug console. Angular correctly renders the component property unless system-polyfill is included, in which case it will stop at 1 and never change it to 2 or 3, even though the console shows that the property is in fact changed:

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

I created the following Plunker to demonstrate this behaviour: http://plnkr.co/edit/GR9U9fTctmkSGsPTySAI?p=preview

And yeah, the obvious solutions are:

  1. Don't manually include system-polyfills, or
  2. Manually include a differeny Promise polyfill before SystemJS if you need it

But I'm still curious why this is happening, and hopefully somebody can shed some light on this (and possibly help to resolve the base issue).

Edit: The original title of this was Why is Angular 2's template rendering misbehaving when using system-polyfills (when.js). Thanks to Thierry and Günter for contributing and pointing out that Angular 2's use of zones is at play here. For anybody who comes across this in the future, here are two excellent articles that explain zones in further detail and will enhance your understanding of this scenario should you run into it:

  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

Solution

  • Promise polyfill provides a custom implementation of Promise that seems to be executed outside the scope of Angular2 (i.e. not in a zone).

    If you execute your code explicitly within a zone managed by Angular2 (with the NgZone class), it works when the system-polyfill.js file is included.

    Here is a sample:

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

    See the corresponding plunkr: http://plnkr.co/edit/EJgZKWVx6FURrelMEzN0?p=preview.