Почему шаблон Angular 2 не обновляется при вызовах вне угловой зоны?

Я думал, что создам очень простую форму входа с привязанными к компоненту свойствами имени пользователя и пароля, которая будет выполнять следующие шаги:

  1. Отправить учетные данные с вызовом fetch()
  2. ЗАТЕМ получите содержимое JSON объекта результата ответа
  3. ТОГДА проверьте этот контент на предмет результата проверки на стороне сервера

Если бы учетные данные были неправильными, это изменило бы свойство компонента, которое сообщит Angular применить класс к входам имени пользователя и пароля, чтобы временно сделать их красными (используя setTimeout, чтобы изменить это обратно)

Проблема, с которой я столкнулся, заключается в том, что Angular неправильно применяет класс, и я не знал, почему. Я решил создать упрощенный тестовый проект, чтобы сузить проблему, и в итоге включение 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')
            });
        });
    }
}

Я создал следующий плункер, чтобы продемонстрировать это поведение: =предварительный просмотр

И да, очевидные решения:

  1. Не включайте системные полифиллы вручную или
  2. Вручную включите другой полифилл Promise перед SystemJS, если вам это нужно.

Но мне все еще любопытно, почему это происходит, и, надеюсь, кто-нибудь может пролить свет на это (и, возможно, помочь решить базовую проблему).

Редактировать. Первоначальный заголовок был Why is Angular 2's template rendering misbehaving when using system-polyfills (when.js). Спасибо Тьерри и Гюнтеру за вклад и указание на то, что здесь играет роль использование зон в 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

person dgbonomo    schedule 18.04.2016    source источник


Ответы (2)


Promise polyfill предоставляет пользовательскую реализацию Promise, которая, кажется, выполняется за пределами Angular2 (т.е. не в зоне).

Если вы явно выполняете свой код в зоне, управляемой Angular2 (с классом NgZone), он работает, когда включен файл 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.

person Thierry Templier    schedule 18.04.2016

обновить

Больше не уверен в объяснении, но обходной путь работает. Но я думаю, что есть какая-то другая основная проблема.

См. также 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'));
            });
        });
    }
}

Пример планкера

person Günter Zöchbauer    schedule 18.04.2016