Декоратор Output в Angular: передача событий и данных от дочернего к родительскому компоненту
Декоратор Output позволяет дочернему компоненту отправлять события и данные в родительский компонент через EventEmitter. Это простой и типобезопасный способ коммуникации «снизу вверх» в Angular — пригодится для уведомлений, передачи списков или любых небольших пакетов данных. В статье — пошаговый пример, проверки, альтернативы и рекомендации по отладке и безопасности.
Введение

В Angular веб-страница обычно состоит из множества переиспользуемых компонентов. Каждый компонент имеет свою TypeScript-логику, шаблон HTML и CSS-стили. Часто нужно, чтобы дочерний компонент уведомил родителя о каком-то действии или передал данные — для этого используется декоратор Output и класс EventEmitter.
Короткие определения:
- Output — декоратор для публичного события компонента.
- EventEmitter
— класс, через который компонент испускает (emit) события типа T.
Важно: Output передаёт данные только в направлении «дочерний → родитель» через привязку события в шаблоне родителя.
Когда использовать Output
- Нужны локальные уведомления (клик, выбор, подтверждение) от дочернего к родительскому компоненту.
- Передаётся небольшая порция данных (например, id, массив имён, объект формы).
- Не требуется глобальное состояние или кэширование между удалёнными компонентами.
Важно: если нужно делиться состоянием между несвязанными компонентами (без иерархии), лучше использовать сервисы, RxJS или хранилище (NgRx/Vikx).
Как добавить декоратор Output в дочерний компонент
Ниже — шаг за шагом с примером. Код-блоки сохранены без изменений.
Создайте новое приложение Angular с родительским компонентом (по умолчанию — app) и дочерним компонентом.
Замените содержимое app.component.html:
This is the parent component
- Стили для родительского компонента (app.component.css):
.parent-component {
font-family: Arial, Helvetica, sans-serif;
background-color: lightcoral;
padding: 20px
}- Создайте дочерний компонент командой:
ng g c data-component- Замените содержимое data-component.component.html, добавив кнопку и привязав её к функции onButtonClick():
This is the child component
- Стили для дочернего компонента (data-component.component.css):
.child-component {
font-family: Arial, Helvetica, sans-serif;
background-color: lightblue;
margin: 20px;
padding: 20px
}- В файле data-component.component.ts добавьте заготовку функции:
onButtonClick() {
}- Импортируйте Output и EventEmitter:
import { Component, Output, EventEmitter, OnInit } from '@angular/core';- Объявите свойство Output в классе дочернего компонента:
export class DataComponentComponent implements OnInit {
@Output() buttonWasClicked = new EventEmitter();
// ...
} - Вызовите emit() внутри onButtonClick(), чтобы уведомить родителя:
onButtonClick() {
this.buttonWasClicked.emit();
}Как слушать события дочернего компонента из родителя
Чтобы обработать событие, нужно подписаться на него в шаблоне родителя:
- Используйте дочерний компонент в app.component.html и подпишитеся на событие buttonWasClicked:
- Добавьте обработчик в app.component.ts:
message: string = ""
buttonInChildComponentWasClicked() {
this.message = 'The button in the child component was clicked';
}- Отобразите сообщение в шаблоне:
{{message}}
- Запустите приложение командой ng serve и откройте http://localhost:4200. Родитель и ребёнок будут с разными фонами для наглядности.
- Нажмите кнопку Click me — событие отправится вверх, и родитель отобразит сообщение.
Как передать данные от дочернего компонента к родительскому
Output может передавать не только события без нагрузки, но и данные любого типа T.
- В data-component.component.ts объявите массив строк:
listOfPeople: string[] = ['Joey', 'John', 'James'];- Измените тип EventEmitter с void на string[]:
@Output() buttonWasClicked = new EventEmitter(); - Передавайте данные через emit():
onButtonClick() {
this.buttonWasClicked.emit(this.listOfPeople);
}- В шаблоне родителя получайте $event:
- Обновите обработчик в app.component.ts:
buttonInChildComponentWasClicked(dataFromChild: string[]) {
this.message = 'The button in the child component was clicked';
}- Сохраните пришедшие данные в свойстве data:
message: string = ""
data: string[] = []
buttonInChildComponentWasClicked(dataFromChild: string[]) {
this.message = 'The button in the child component was clicked';
this.data = dataFromChild;
}- Отобразите данные в шаблоне:
{{data.join(', ')}}
- Запустите приложение и проверьте: по клику родитель получит массив имён и отобразит их.
Частые ошибки и отладка (Когда это не работает)
- Неправильно импортирован Output/EventEmitter или опечатка в импорте. Проверьте: import { Output, EventEmitter } from ‘@angular/core’.
- Отсутствует привязка события в шаблоне родителя: убедитесь, что вы используете (buttonWasClicked) в теге дочернего компонента.
- Неправильный тип Generic в EventEmitter
— TypeScript не бросит runtime-ошибку, но данные могут быть не того вида. Совпадение типов помогает избежать багов. - Ошибка: дочерний компонент не включён в шаблон родителя (не вставлен тег) или модуль не объявляет компонент.
- Если компонент создаётся динамически через ViewContainerRef, подписка может требовать ручной привязки.
Отладочные шаги:
- Добавьте console.log в onButtonClick() и в обработчике родителя.
- Временно используйте EventEmitter
для проверки структуры данных. - Проверьте, что селектор у дочернего компонента совпадает с используемым тегом.
Альтернативы и когда их использовать
Сервисы с RxJS (Subject / BehaviorSubject)
- Подходящ, когда нужно обмениваться данными между несвязанными компонентами или сохранить состояние.
- Преимущества: централизованное состояние, историчность (BehaviorSubject держит последнее значение).
- Недостаток: выше связность через сервис, сложнее проследить поток событий при отладке.
Input + двухсторонняя привязка (по шаблону)
- Используйте Input, если данные должны течь сверху вниз; комбинируйте с Output для двусторонней связи.
Управляемое хранилище (NgRx, Akita и др.)
- Подходит для крупных приложений с множеством источников правки и сложной бизнес-логикой.
Event Bus (глобальные события)
- Рекомендуется избегать в большинстве случаев: трудно отследить зависимости и отладить.
Шаблон действий (мини-методология)
- Определите направление данных: дочерний → родитель? используйте Output.
- Выберите тип данных T и укажите его в EventEmitter
. - На дочернем компоненте вызывайте emit(data).
- На родительском компоненте подпишитесь в шаблоне через (event)=”handler($event)”.
- Протестируйте: unit + интеграционные тесты (см. раздел тестов).
Критерии приёмки
- Нажатие на кнопку в дочернем компоненте вызывает метод onButtonClick().
- Вызов onButtonClick() испускает событие через buttonWasClicked.emit() с ожидаемым payload.
- Родитель получает событие и сохраняет/отображает полученные данные.
- Нет ошибок в консоли браузера при исполнении сценария.
Контрольный список для разработчика
- Импортированы Output и EventEmitter из @angular/core.
- EventEmitter имеет корректный тип (
). - Вызов emit() выполняется в нужной точке логики.
- Родитель использует (eventName) в шаблоне.
- Обработчик родителя принимает аргумент и безопасно его использует.
- Добавлены unit-тесты на emit и обработку события.
Тестовые случаи / Критерии приёмки (минимальные)
- При клике вызывается onButtonClick() — проверка spy/spyOn.
- После emit родительский метод получает корректный параметр ($event).
- Если передаётся массив, он не мутируется при приёме (проверить неизменность, если нужно).
Шаблоны кода — быстрый cheat sheet
Простой EventEmitter (без данных):
@Output() closed = new EventEmitter();
close() {
this.closed.emit();
} EventEmitter с данными:
@Output() selected = new EventEmitter();
select(id: number) {
this.selected.emit(id);
} Подписка в родителе:
Безопасность и приватность данных
- Не отправляйте через Output чувствительные данные (пароли, токены). События проходят через приложение и могут быть прочитаны в консоли при неправильной обработке.
- Для сложных объектов думайте об их клонировании при отправке, чтобы избежать непреднамеренных мутаций (Object.assign / spread / JSON clone).
Производительность и масштабируемость
- Частые события с большими объектами приводят к копированию и нагрузке. Отправляйте минимальный объём данных (id вместо полного объекта).
- Для высокочастотных обновлений используйте сервисы и потоковые решения (throttle/debounce, RxJS).
Примеры, когда Output не подходит (контрпримеры)
- Нужен общий доступ к состоянию между несвязанными компонентами — лучше сервис.
- Понадобится сохранять историю изменений — используйте BehaviorSubject/Store.
- Компоненты находятся в разных фреймворках/песочницах — Output не пересечёт границ iframe.
Рекомендации по стилю и архитектуре
- Всегда типизируйте EventEmitter
. - Не используйте Output для изменения глобального состояния напрямую — делегируйте обновления сервисам.
- Документируйте контракт события: что содержит payload и какие гарантии формата.
Маленькая памятка (1‑строчная глоссарий)
- Output — «входящие» события дочернего компонента, которые родитель может слушать; EventEmitter — механизм их отправки.
Короткое руководство к внедрению в проект (SOP)
- Создать компонент или определить существующий.
- Добавить @Output() имяСобытия = new EventEmitter<Тип>();
- Вызывать this.имяСобытия.emit(данные) при нужном действии.
- В родительском шаблоне повесить (имяСобытия)=”обработчик($event)”.
- Протестировать и покрыть unit-тестами.
Итог
Декоратор Output — это простой, типобезопасный способ организовать связь «дочерний → родитель» в Angular. Для передачи состояния между несвязанными компонентами используйте сервисы или паттерны управления состоянием. Соблюдайте правила типизации и не передавайте через события чувствительные данные.
Короткие выводы:
- Output + EventEmitter — лучший выбор для локальных уведомлений.
- Типизация EventEmitter повышает надёжность.
- Для глобального состояния — сервисы и RxJS.
Спасибо за чтение — применяйте Output осознанно и тестируйте интеграцию компонентов.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone