Angular

Сигнальные запросы: viewChild и contentChild

Компонент формы должен после загрузки автоматически поставить фокус в первое поле ввода. Для этого нужна ссылка на DOM-элемент input. Раньше её получали через декоратор ViewChild и хук ngAfterViewInit, и нередко натыкались на ошибку: на момент ngOnInit ссылка ещё undefined. Сигнальные запросы решили это иначе. viewChild возвращает сигнал, который сам обновляется, когда элемент появляется, и интегрируется со всей реактивной системой - его можно читать в computed и effect наравне с любым signal.

  • Установка фокуса: ссылка на поле ввода, чтобы вызвать focus() после отрисовки
  • Интеграция с DOM-библиотеками: получение canvas или контейнера для графика, карты, редактора
  • Доступ к дочернему компоненту: вызов его публичного метода или чтение его сигналов из родителя
  • Проекция контента: компонент-обёртка читает спроецированные элементы через contentChildren
  • Измерение и прокрутка: доступ к элементу для getBoundingClientRect или scrollIntoView

Предварительные знания

  • Понимание сигналов: чтение через вызов, интеграция с computed и effect
  • Знание шаблонных ссылок (#ref) в разметке Angular
  • Базовое представление о проекции контента через ng-content

От декоратора ViewChild к сигнальным запросам

С первых версий Angular доступ к дочерним элементам и компонентам шёл через декораторы @ViewChild, @ViewChildren, @ContentChild и @ContentChildren. Они работали, но имели шероховатости: значение появлялось только после определённого хука жизненного цикла, требовался дополнительный параметр static, а изменения отслеживались отдельной системой QueryList, не связанной с сигналами. С приходом сигналов команда Angular ввела функции-запросы viewChild, viewChildren, contentChild и contentChildren, возвращающие сигналы. Они вышли в developer preview в версии 17, а в версии 19 (конец 2024 года) стали стабильными и пригодными для продакшена, заняв место предпочтительного способа запроса элементов.

viewChild и viewChildren: элементы своего шаблона

viewChild запрашивает один элемент или компонент из шаблона самого компонента и возвращает сигнал. Цель запроса задают шаблонной ссылкой (#ref) или типом компонента или директивы. До того как элемент появится в DOM, сигнал отдаёт undefined, а после появления - найденное значение. viewChildren возвращает сигнал с массивом всех совпадений.

Поскольку box - это сигнал, его читают вызовом box(), и он undefined до отрисовки. Поэтому фокус ставят в afterNextRender, когда элемент уже существует, и применяют опциональную цепочку. Запрос компонента вместо элемента выглядит так же: указывается класс компонента, и сигнал отдаёт его экземпляр для вызова публичных методов.

Главное преимущество сигнальной формы: результат запроса - обычный сигнал. Его можно подставить в computed (как tabCount выше) или прочитать в effect, и реактивная система сама обновит зависимое значение, когда набор элементов изменится.

Что возвращает viewChild и как это читают?

contentChild и required-вариант

viewChild ищет в собственном шаблоне компонента. Но компонент-обёртка часто получает контент извне через ng-content - это называется проекцией. Для доступа к спроецированным элементам служат contentChild и contentChildren. Разница только в области поиска: view - это то, что компонент объявил у себя в шаблоне, content - то, что вложили внутрь его тегов снаружи.

  • viewChild / viewChildren — Ищет элементы и компоненты, объявленные в шаблоне самого компонента
  • contentChild / contentChildren — Ищет элементы, спроецированные внутрь компонента снаружи через ng-content

У одиночных запросов есть вариант required. Обычный viewChild имеет тип, включающий undefined, потому что элемент может отсутствовать. Если по логике элемент обязан быть всегда (например, он жёстко записан в шаблоне), используют viewChild.required или contentChild.required. Такой сигнал имеет тип без undefined, и его не нужно проверять при чтении.

required не магия: если помеченный обязательным элемент на самом деле отсутствует на момент чтения, Angular выбросит ошибку. Помечать обязательным стоит только то, что гарантированно есть в шаблоне, иначе типобезопасность обернётся падением в рантайме.

Чем contentChild отличается от viewChild по области поиска?

Сравнение со старым декоратором ViewChild

Старый подход использовал декоратор @ViewChild над свойством. Значение появлялось только после хука ngAfterViewInit, а для доступа в более раннем хуке требовался параметр static: true. Изменения набора элементов отслеживались через QueryList - отдельный механизм с собственной подпиской changes, не связанный с сигналами. Сигнальная форма убирает эти сложности.

Аспект@ViewChild (декоратор)viewChild (функция)
Тип результатаСвойство, заполняемое позжеСигнал, читаемый вызовом
Когда доступноПосле ngAfterViewInitРеактивно, обновляется само
Параметр staticИногда нуженНе нужен
Отслеживание набораQueryList и подписка changesСигнал, работает в computed и effect
Интеграция с сигналамиНетПолная

В новом коде на Angular 21 по умолчанию выбирают сигнальные запросы. Декораторы ViewChild и ContentChild остаются рабочими ради совместимости, но новые компоненты пишут на viewChild и contentChild, чтобы доступ к элементам жил в одной реактивной системе со всем остальным состоянием.

Какое преимущество даёт сигнальный viewChild по сравнению со старым декоратором @ViewChild?

Связь с другими темами

Сигнальные запросы встраивают доступ к DOM и дочерним компонентам в реактивную модель:

  • Сигналы — viewChild возвращает сигнал, который читают вызовом и используют в computed и effect
  • Жизненный цикл и afterRender — afterNextRender даёт безопасный момент работать с DOM-элементом, полученным запросом
  • computed — Результат запроса можно подставить в computed, чтобы реагировать на появление элемента

Итог

  • viewChild и viewChildren запрашивают элементы и компоненты из собственного шаблона компонента
  • contentChild и contentChildren запрашивают элементы, спроецированные в компонент через ng-content
  • Все четыре функции возвращают сигналы: одиночные дают значение или undefined, множественные дают массив
  • viewChild.required и contentChild.required гарантируют наличие значения, убирая undefined из типа
  • Сигнальные запросы стабильны с версии 19, интегрированы с computed и effect и предпочтительнее старых декораторов ViewChild и ContentChild

Связанные уроки

  • ng-11-signals-intro — Сигнальные запросы возвращают сигналы, поэтому нужно понимать чтение сигнала через вызов
  • ng-10-lifecycle — afterNextRender даёт безопасный момент для работы с элементом, полученным через viewChild
  • ng-12-computed — Результат viewChild можно использовать в computed, как любой другой сигнал
Сигнальные запросы: viewChild и contentChild

0

1

Войти