Angular

Связь компонентов и проекция контента

Компонент-модалка должна показывать любое содержимое: форму, текст, картинку. Если описать всё внутри неё, она перестанет быть переиспользуемой. Нужен механизм, который позволит родителю задать содержимое снаружи, а модалке - лишь обернуть его в свою рамку с заголовком и кнопкой закрытия. Это проекция контента: компонент-обёртка резервирует слоты, а наполняет их вызывающий код. Так строятся карточки, диалоги, панели и вкладки в любой дизайн-системе.

  • Модальные окна: обёртка задаёт рамку и крестик, а содержимое приходит снаружи через проекцию
  • Карточки: компонент Card проецирует заголовок, тело и подвал в отдельные слоты
  • Вкладки и аккордеоны: контейнер управляет переключением, а контент каждой вкладки задаёт родитель
  • Кнопки дизайн-системы: проекция позволяет вставить текст и иконку внутрь стилизованной кнопки
  • Layout-компоненты: шапка, боковая панель и основная область заполняются проекцией из страницы

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

  • Урок ng-06: сигнальные input и output
  • Урок ng-03: вложение компонентов и selector
  • Понимание привязок свойств и событий из урока ng-04
  • Идея переиспользуемого компонента-обёртки

Диалог родителя и ребёнка

Базовая связь компонентов однонаправленна по данным: родитель передаёт значения вниз через сигнальные input, ребёнок сообщает о событиях вверх через output. Родитель остаётся владельцем состояния, ребёнок лишь отображает данные и эмитит события. Это делает поток предсказуемым: чтобы понять, откуда у ребёнка данные, достаточно посмотреть на привязки в шаблоне родителя.

Родитель хранит список и метод markDone, ребёнок не знает, как именно обрабатывается событие. Такая изоляция позволяет переиспользовать TodoRow в любом контексте: важно лишь, что он принимает todo и эмитит toggle. Это контракт компонента, выраженный его входами и выходами.

Если данные надо разделить между далёкими компонентами, не связанными родством, цепочка input и output становится громоздкой. Тогда применяют общий сервис через внедрение зависимостей или сигнальный store. Но для прямой связи родитель-ребёнок входов и выходов достаточно.

Кто владеет состоянием в типичной паре родитель-ребёнок на входах и выходах?

Проекция через ng-content

Входы передают данные, но иногда родителю нужно вложить целый кусок разметки внутрь компонента-обёртки. Для этого служит ng-content: компонент-обёртка ставит этот тег как слот, и всё, что родитель вложил между открывающим и закрывающим тегами компонента, проецируется в это место. Обёртка не знает заранее, что именно туда попадёт.

В отличие от входа, который передаёт данные, проекция передаёт готовую разметку. Это разные инструменты: input хорош для значений (имя, число, объект), а ng-content - для произвольного содержимого, которое рисует сам родитель. Часто их сочетают: данные через input, оформление через проекцию.

Спроецированное содержимое остаётся в контексте родителя: его привязки и обработчики событий работают так, как будто оно лежит в шаблоне родителя, а не обёртки. Обёртка лишь определяет, где это содержимое окажется визуально.

Что делает тег ng-content в шаблоне компонента-обёртки?

Несколько слотов и ngProjectAs

Когда обёртке нужно разложить содержимое по местам - например, заголовок в шапку, текст в тело, кнопки в подвал - применяют несколько ng-content с атрибутом select. select это CSS-селектор: содержимое, подходящее под него, попадает именно в этот слот. Один ng-content без select служит слотом по умолчанию для всего остального.

Атрибут panel-title на элементе сопоставляется с select="[panel-title]" и направляет его в шапку. Содержимое без помеченных атрибутов уходит в слот по умолчанию. Иногда элемент нельзя пометить нужным селектором напрямую, например когда это компонент с фиксированным тегом. Тогда применяют ngProjectAs: он перенаправляет элемент в слот так, будто у него другой селектор.

Если содержимое не подходит ни под один select и слота по умолчанию нет, оно просто не отрисуется. При нескольких слотах стоит либо предусмотреть слот по умолчанию, либо чётко документировать ожидаемые селекторы для каждого слота.

Зачем нужен атрибут select у ng-content?

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

Эта тема объединяет передачу данных и вложение разметки:

  • Сигнальные input и output — Данные между родителем и ребёнком ходят через входы и выходы
  • Компоненты — Проекция вкладывает содержимое внутрь компонента-обёртки
  • Директивы — hostDirectives и атрибутивные директивы дополняют поведение проецируемых элементов

Итог

  • Родитель и ребёнок общаются через сигнальные input (данные вниз) и output (события вверх) - это основной канал связи
  • ng-content резервирует слот, куда попадает содержимое, вложенное родителем в тег компонента
  • Несколько слотов разделяются атрибутом select, который выбирает содержимое по CSS-селектору
  • Содержимое без подходящего select попадает в слот по умолчанию, если он есть
  • ngProjectAs перенаправляет элемент в нужный слот, когда его собственный селектор не совпадает с select

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

  • ng-06-inputs-outputs — Связь родителя и ребёнка строится на сигнальных входах и выходах из прошлого урока
  • ng-03-components — Проекция вкладывает содержимое в компонент, чьё устройство разобрано ранее
Связь компонентов и проекция контента

0

1

Войти