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

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

  1. Отправить учетные данные с вызовом fetch()
  2. ТОГДА получить содержимое JSON объекта результата Response
  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')
            });
        });
    }
}

Я создал следующий Plunker, чтобы продемонстрировать это поведение: http://plnkr.co/edit/GR9U9fTctmkSGsPTySAI?p=preview

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

  1. Не включайте system-polyfills или
  2. Вручную добавьте другой Polyfill 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

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.

Обновить

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

Смотри также https://github.com/angular/angular/issues/7792

оригинал

Я предполагаю, что проблема вызвана 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'));
            });
        });
    }
}

Пример плунжера

Другие вопросы по тегам