Angular

Сигнальные input() и output()

Карточка товара должна показывать переданный ей продукт и сообщать наверх о клике Купить. Родитель передаёт данные вниз, ребёнок шлёт события вверх - это базовый диалог компонентов. Годами его писали через декораторы @Input и @Output, но входное значение было обычным полем, и чтобы среагировать на его изменение, приходилось городить ngOnChanges. Сигнальные input() и output() сделали вход реактивным значением: он сам уведомляет computed и effect, когда родитель прислал новое.

  • Карточки товаров: родитель-список передаёт каждый товар в дочернюю карточку через input
  • Кнопки действий: дочерний компонент шлёт наверх событие через output по клику пользователя
  • Поля форм: кастомный компонент-поле через model двусторонне связывается со значением родителя
  • Таблицы: строка-компонент получает данные через input и сообщает о выборе через output
  • Дизайн-системы: переиспользуемые компоненты принимают конфигурацию через input.required, гарантируя обязательность

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

  • Урок ng-03: компоненты, их вложение и selector
  • Понимание привязки свойств и событий из урока ng-04
  • Идея сигнала из урока ng-01: значение, читаемое как функция
  • Базовый TypeScript: дженерики и типы на уровне идеи

input() и input.required()

Вход компонента объявляется функцией input(). Она возвращает сигнал только для чтения: значение читается как функция, и когда родитель присылает новое, сигнал уведомляет всех, кто от него зависит. В декоратор @Component добавлять ничего не нужно - функция сама регистрирует вход.

Функция input(значение) задаёт значение по умолчанию, если родитель ничего не передал. input.required() делает вход обязательным: если родитель забудет его указать, компилятор шаблона выдаст ошибку. Это убирает целый класс багов с undefined-значениями в обязательных данных.

Поскольку вход - сигнал, на нём напрямую строится производное состояние через computed. Раньше для реакции на изменение входа писали хук жизненного цикла ngOnChanges, теперь достаточно computed, который сам пересчитается при новом значении.

Вход-сигнал доступен только для чтения внутри компонента: значение задаёт родитель, а ребёнок его лишь читает. Это сохраняет односторонний поток данных вниз и делает источник правды очевидным - им владеет родитель.

Что возвращает функция input() при объявлении входа компонента?

output() и события наверх

Если вход передаёт данные вниз, то выход шлёт события вверх. Функция output() создаёт эмиттер: дочерний компонент вызывает на нём emit с полезной нагрузкой, а родитель слушает это как обычное событие через круглые скобки. Так ребёнок сообщает родителю о клике, выборе или завершении действия, не зная, кто его слушает.

Переменная event в обработчике несёт значение, переданное в emit. Тип события задаётся дженериком output<Product>, поэтому родитель получает строго типизированную нагрузку. Симметрия с шаблоном сохраняется: вход через [prop], выход через (event), как у обычных DOM-привязок.

Функция output() пришла на смену декоратору @Output с EventEmitter. Под капотом она проще и не привязана к RxJS, хотя при необходимости выход можно создать и из observable через outputFromObservable.

Как дочерний компонент сообщает родителю о событии через output()?

model() для двусторонней связи

Иногда компоненту нужен и вход, и обратное уведомление об изменении одного значения - классический случай кастомного поля формы. Функция model() создаёт двусторонний вход-сигнал: внутри компонента он изменяемый через set, а снаружи родитель связывает его синтаксисом банан-в-коробке, как у ngModel.

Отличие model от input в том, что model изменяемый внутри компонента и при изменении автоматически уведомляет родителя. Это удобно именно для обёрток над элементами ввода, где значение по природе двунаправленное. Для данных, которые идут только сверху вниз, по-прежнему используют обычный input.

ФункцияНаправлениеЗаменила
input()Сверху вниз, только чтение в ребёнке@Input
input.required()Сверху вниз, обязателен@Input({ required: true })
output()Снизу вверх, событие@Output + EventEmitter
model()В обе стороны, банан-в-коробке@Input + @Output пара

Чем model() отличается от обычного input()?

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

Входы и выходы - канал общения между компонентами:

  • Связь компонентов и проекция — На input и output строится взаимодействие родителя и ребёнка
  • Шаблоны и привязки — Родитель передаёт input через [prop] и слушает output через (event)
  • Зачем Angular — Сигнальные входы - часть сигнальной модели всего фреймворка

Итог

  • input() объявляет вход компонента как сигнал, который читается как функция и реагирует на новые значения от родителя
  • input.required() делает вход обязательным: без него компонент не скомпилируется
  • output() создаёт событие, которое дочерний компонент эмитит вверх через emit, а родитель слушает через привязку события
  • model() даёт двусторонний вход-сигнал для синтаксиса банан-в-коробке, как у ngModel
  • Сигнальные функции заменили декораторы @Input и @Output, стали production-ready с v19 и интегрированы с computed и effect

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

  • ng-03-components — Входы и выходы соединяют родительский и дочерний компоненты, разобранные ранее
  • ng-01-why-angular — Сигнальные входы развивают сигнальную модель Angular из первого урока
  • ng-07-component-communication — На входах и выходах строится вся связь компонентов и проекция контента
Сигнальные input() и output()

0

1

Войти