вот что происходит;
в режиме разработки происходят два последовательных цикла CD, как объяснено в Соответствующие операции обнаружения изменений раздел этой статьи;
Работающее приложение Angular - это дерево компонентов. Во время обнаружения изменений Angular выполняет проверки для каждого компонента, который состоит из следующих операций, выполняемых в указанном порядке:
- обновить связанные свойства для всех дочерних компонентов / директив
- вызовите перехватчики жизненного цикла ngOnInit, OnChanges и ngDoCheck для всех дочерних компонентов / директив
- обновить DOM для текущего компонента
- запуск обнаружения изменений для дочернего компонента
- вызывать хук жизненного цикла ngAfterViewInit для всех дочерних компонентов / директив
- после нажатия кнопки добавления, когда завершается обратный вызов, выполняется обнаружение изменений.
comp
имеет 1 элемент
- во время первого цикла CD; DOM обновляется на шаге 3. элементы в
comp
добавляются в DOM, а {{ count.length }}
проецируется в DOM как 0
@ViewChildren('comp') test: QueryList<ElementRef>
обновляется непосредственно перед шагом 5. test: QueryList
имеет 1 элемент
QueryList.changes
наблюдаемый издает одновременно с запросом ViewChildren
, непосредственно перед выполнением шага 5. this.count.push(this.test.length)
выполняется и count.length
становится 1.
ngAfterViewChecked
выполняется, и первый CD (соответствующая часть нашего расследования) заканчивается
- Начинается второй цикл CD. во время этого цикла угловая проверка предыдущих значений 1-го цикла CD с текущими значениями.
- Во время этой проверки он обнаружил, что
{{ count.length }}
было спроецировано на DOM как 0, но теперь count.length
равно 1. В этот момент выдается исключение.
Чтобы объяснить больше; изменение {{ count.length }}
на {{ count }}
не выдает ошибку, потому что ссылка на объект не изменилась.
* * Аналогично одна тысяча пятьдесят шесть; изменение
{{ count.length }}
на
{{ comps.length }}
также не выдает ошибку, поскольку
comps.length
также было равно 1 до начала 1-го цикла CD.
Я также создал демонстрационную версию, которая печатает соответствующие шаги в течение жизненного цикла компонента, связанные с нашим исследованием. Вы можете видеть, что исключение выдается до начала 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));