В каком порядке вызываются хуки в родительских и дочерних компонентах Angular
Что проверяют
Знаете ли вы реальный порядок хуков, а не просто их список, и понимаете ли, почему он такой: Angular проверяет дерево сверху вниз, а представление родителя готово только после детей. Следом часто спрашивают, где безопасно обращаться к @ViewChild и откуда берётся ExpressionChangedAfterItHasBeenCheckedError.
Как отвечать
При первом рендере родителя с дочерним компонентом в шаблоне порядок такой:
- родитель:
ngOnChanges(только если у него есть привязанные@Input),ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked; - дочерний:
ngOnChanges,ngOnInit,ngDoCheck,ngAfterContentInit,ngAfterContentChecked,ngAfterViewInit,ngAfterViewChecked; - снова родитель:
ngAfterViewInit,ngAfterViewChecked.
Логика простая: инициализация идёт сверху вниз, а ngAfterViewInit родителя срабатывает последним, потому что его представление включает детей. Поэтому @ViewChild доступен в ngAfterViewInit (или уже в ngOnInit, если указать static: true).
На каждом следующем цикле проверки изменений повторяются ngDoCheck и оба Checked-хука в том же порядке, а ngOnChanges вызывается, только если у входного свойства сменилась ссылка.
Для контента из ng-content порядок обратный: ngAfterContentInit у спроецированного компонента срабатывает раньше, чем у родителя, в которого его вставили.
При удалении ngOnDestroy вызывается сначала у дочерних компонентов, затем у родителя, но строить на этом логику не стоит.
Где спотыкаются
Говорят, что ngAfterViewInit идёт сначала у родителя, потом у ребёнка. Это неверно, и это самая частая ошибка.
Ждут ngOnChanges после мутации объекта во @Input. Хук реагирует на новую ссылку, а изменение поля внутри того же объекта не видит.
Меняют в дочернем ngAfterViewInit данные, привязанные к шаблону родителя, и получают ExpressionChangedAfterItHasBeenCheckedError в режиме разработки.