Событие изменения Angular QueryList

При подписке на события изменения QueryList и при реагировании на событие и изменении свойства, к которому привязано представление. Я получаю ExpressionChangedAfterItHasBeenCheckedError, и значение похоже на старое значение.

Я сделал stackblitz пример, показывающий ошибку.

Я могу решить это, добавив микрозадачу в очередь. Но я просто пытаюсь понять, что происходит.

Спасибо


person pdiddy    schedule 14.06.2019    source источник
comment
Вы ознакомились с этой статьей? ? См. этот stackblitz для возможного обходного пути с ChangeDetectorRef.detectChanges.   -  person ConnorsFan    schedule 15.06.2019
comment
Кажется, это связано с обнаружением изменений Angular. Вы можете ознакомиться с этой статьей здесь. stackoverflow.com/questions/38445670/   -  person Joseph Wu    schedule 15.06.2019


Ответы (1)


вот что происходит;

в режиме разработки происходят два последовательных цикла CD, как описано в Соответствующие операции по обнаружению изменений этой статьи;

Работающее приложение Angular представляет собой дерево компонентов. Во время обнаружения изменений Angular выполняет проверки для каждого компонента, которые состоят из следующих операций, выполняемых в указанном порядке:

  1. обновить связанные свойства для всех дочерних компонентов/директив
  2. вызывать хуки жизненного цикла ngOnInit, OnChanges и ngDoCheck для всех дочерних компонентов/директив
  3. обновить DOM для текущего компонента
  4. запустить обнаружение изменений для дочернего компонента
  5. вызвать крючок жизненного цикла ngAfterViewInit для всех дочерних компонентов/директив
  • после того, как вы нажмете кнопку «Добавить», когда обратный вызов завершает выполнение, начинается обнаружение изменений. comp содержит 1 элемент
  • во время первого цикла CD; DOM обновляется на шаге 3. элементы в comp добавляются в DOM, а {{ count.length }} проецируются в DOM как 0
  • @ViewChildren('comp') test: QueryList<ElementRef> обновляется непосредственно перед шагом 5. test: QueryList содержит 1 элемент
  • QueryList.changes observable генерируется одновременно с установленным ViewChildren запросом, непосредственно перед шагом 5. this.count.push(this.test.length) выполняется, а count.length становится равным 1.
  • ngAfterViewChecked выполняется, и первый компакт-диск (соответствующая часть нашего расследования) заканчивается
  • Начинается 2-й цикл CD. во время этого цикла angular проверяет предыдущие значения 1-го цикла CD с текущими значениями.
  • Во время этой проверки он обнаруживает, что {{ count.length }} был спроецирован в DOM как 0, но теперь count.length равен 1. В этот момент возникает исключение.

Чтобы объяснить больше; изменение {{ count.length }} на {{ count }} не вызывает ошибку, поскольку ссылка на объект не изменилась.

Сходным образом; изменение {{ count.length }} на {{ comps.length }} также не вызывает ошибки, потому что comps.length также было 1 до начала 1-го цикла компакт-диска.

Я также создал демонстрацию, которая печатает соответствующие шаги в течение жизненного цикла компонента, связанного с нашим исследованием. Вы можете видеть, что исключение выдается до начала 4-го цикла CD. (1-й и 2-й циклы CD происходят до нажатия кнопки, поэтому в демо-версии следует соблюдать 3-й и 4-й циклы CD). Также обратите внимание на точку, где QueryList.changes выдает значение.

https://stackblitz.com/edit/angular-tmcttm

Наконец, как вы сказали, решение этой конкретной проблемы состоит в том, чтобы добавить микрозадачу в очередь, изменив эту строку

this.count.push(this.test.length);

в это

setTimeout(_ => this.count.push(this.test.length));

person ysf    schedule 14.06.2019
comment
хорошее объяснение. интересно, почему вызов микрозадачи будет работать? - person Joseph Wu; 15.06.2019
comment
в основном, помещая код в settimeout, он не выполняется в текущем цикле CD. он добавляется в очередь задач и выполняется в следующем цикле CD. это всесторонне объясняется здесь -16f7a575afef" rel="nofollow noreferrer">blog.angularindepth.com/ - person ysf; 16.06.2019