Подтвердить что ты не робот

Как работает функция поиска Angular 2?

В Angular 1 обнаружение изменений было связано с грязной проверкой иерархии $scope. Мы будем неявно или явно создавать наблюдателей в наших шаблонах, контроллерах или компонентах.

В Angular 2 у нас больше нет $scope, но мы переопределяем setInterval, setTimeout и др. Я могу видеть, как Angular может использовать это для запуска $digest, но как Angular определяет, что изменилось, особенно учитывая, что Object.observe никогда не попадал в браузер?

Пример

Вот простой пример. Объект, определенный в службе, обновляется в setInterval. DOM перекомпилирует каждый интервал.

Как Angular может сказать, что AppComponent наблюдает за службой и что значение атрибута службы изменилось?

var InjectedService = function() {
  var val = {a:1}
  setInterval(() => val.a++, 1000);
  return val;
}

var AppComponent = ng.core
  .Component({
    selector: "app",
    template:
    `
      {{service.a}}
    `
  })
  .Class({
    constructor: function(service) {
      this.service = service;
    }
  })

AppComponent.parameters = [ new ng.core.Inject( InjectedService ) ];

document.addEventListener('DOMContentLoaded', function() {
  ng.platform.browser.bootstrap(AppComponent, [InjectedService])
});
4b9b3361

Ответ 1

Angular создает объект-детектор изменений (см. ChangeDetectorRef) для каждого компонента, который отслеживает последнее значение каждой привязки шаблона, например как {{service.a}}. По умолчанию после каждого асинхронного события браузера (например, ответа с сервера или события клика или события тайм-аута), Angular обнаружение изменений выполняет и проверяет каждую привязку с помощью этих объектов-детекторов изменения.

Если обнаружено изменение, это изменение распространяется. Например.

  • Если значение свойства ввода изменилось, новое значение будет передано в свойство ввода компонента.
  • Если значение привязки {{}} изменилось, новое значение распространяется на свойство DOM textContent.
  • Если значение x изменяется в стиле, атрибуте или привязке класса – то есть [style.x] или [attr.x] или [class.x] – новое значение распространяется на DOM для обновления стиля, атрибута HTML или класса.

Angular использует Zone.js для создания своей собственной зоны (NgZone), в которой обезьяна исправляет все асинхронные события (браузер DOM события, тайм-ауты, AJAX/XHR). Таким образом, обнаружение изменений может автоматически запускаться после каждого асинхронного события. I.e, после завершения каждого обработчика (функции) асинхронного события будет выполняться изменение обнаружения Angular.

У меня есть намного больше деталей и ссылок ссылки в этом ответе: Что такое Angular2, эквивалентный часам AngularJS $?

Ответ 2

Zone.js

Изменения происходят как реакция на что-то, поэтому в этом отношении они асинхронны. Они вызваны асинхронными действиями, и в мире браузера они События. Чтобы перехватить эти события, angular использует zone.js, который исправляет стек вызовов JavaScript (я верю, кто-то поправьте меня, если я ошибаюсь) и выставляет крючки, которые могут быть используется для выполнения других действий.

function angular() {...}
zone.run(angular);

Если вы представляете, что эта функция angular - это весь Angular, это будет то, как она выполняется в зоне. Поступая таким образом, События могут быть перехвачены, и если они запущены, мы можем предположить, что произойдут изменения, и выслушайте их/наблюдайте за ними.

ApplicationRef

В действительности ApplicationRef создает зону:

/**
 * Create an Angular zone.
 */
export function createNgZone(): NgZone {
  return new NgZone({enableLongStackTrace: assertionsEnabled()});
}

и класс NgZone создается с помощью нескольких эмитентов событий:

  this._onTurnStartEvents = new EventEmitter(false);
  this._onTurnDoneEvents = new EventEmitter(false);
  this._onEventDoneEvents = new EventEmitter(false);
  this._onErrorEvents = new EventEmitter(false);

который он предоставляет внешнему миру через getters:

  get onTurnStart(): /* Subject */ any { return this._onTurnStartEvents; }
  get onTurnDone() { return this._onTurnDoneEvents; }
  get onEventDone() { return this._onEventDoneEvents; }
  get onError() { return this._onErrorEvents; }

Когда ApplicationRef создан, он подписывается на события зоны, в частности onTurnDone():

this.zone.onTurnDone
  .subscribe(() => this.zone.run(() => this.tick());

Изменения

Когда запускаются события tick(), функция запускается через каждый компонент:

  this._changeDetectorRefs.forEach((detector) => detector.detectChanges());

и обнаруживает изменения на основе компонентов ChangeDetectionStrategy. Эти изменения собираются как массив объектов SimpleChange:

addChange(changes: {[key: string]: any}, oldValue: any, newValue: any): {[key: string]: any} {
  if (isBlank(changes)) {
    changes = {};
  }
  changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
  return changes;
}

witch доступен для нас через onChanges интерфейс:

export interface OnChanges { 
  ngOnChanges(changes: {[key: string]: SimpleChange}); 
}