Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Выходные события: output()

Выход (output) — это собственное событие компонента. Дочерний компонент не меняет данные родителя сам, а сообщает: «пользователь отметил задачу». Родитель подписывается на это событие так же, как на click, и решает, что делать.

Объявление выхода

Функция output() создаёт событие. В угловых скобках — тип данных, которые оно передаёт:

// src/app/todo-item/todo-item.ts
import { Component, input, output } from '@angular/core';
import { Todo } from '../todo';
 
@Component({
  selector: 'app-todo-item',
  templateUrl: './todo-item.html',
  styleUrl: './todo-item.css',
})
export class TodoItem {
  todo = input.required<Todo>();
  toggle = output<number>();
 
  onToggle() {
    this.toggle.emit(this.todo().id);
  }
}

Метод emit() отправляет событие с номером задачи.

<!-- src/app/todo-item/todo-item.html -->
<label>
  <input type="checkbox" [checked]="todo().done" (change)="onToggle()" />
  {{ todo().title }}
</label>

Щелчок по флажку вызывает onToggle(), а тот — emit().

Подписка в родителе

Родитель пишет имя выхода в круглых скобках. Значение, переданное в emit(), доступно как $event:

<!-- src/app/todo-list/todo-list.html -->
<ul>
  @for (todo of todos(); track todo.id) {
    <li><app-todo-item [todo]="todo" (toggle)="toggleTodo($event)" /></li>
  }
</ul>
// src/app/todo-list/todo-list.ts
toggleTodo(id: number) {
  this.todos.update((list) =>
    list.map((t) => (t.id === id ? { ...t, done: !t.done } : t)),
  );
}

Отметьте флажок — задача станет выполненной, а счётчик «Осталось сделать» из урока о сигналах уменьшится. Данные изменил тот, кто ими владеет, — родитель.

Событие без данных

Если передавать нечего, тип не указывают и emit() вызывают без аргумента. Добавим кнопку удаления:

// src/app/todo-item/todo-item.ts
remove = output();
<!-- src/app/todo-item/todo-item.html -->
<button (click)="remove.emit()">Удалить</button>
<!-- src/app/todo-list/todo-list.html -->
<app-todo-item
  [todo]="todo"
  (toggle)="toggleTodo($event)"
  (remove)="removeTodo(todo.id)"
/>
// src/app/todo-list/todo-list.ts
removeTodo(id: number) {
  this.todos.update((list) => list.filter((t) => t.id !== id));
}

Номер задачи родитель знает сам — он берёт его из переменной todo цикла @for.

Входы и выходы вместе

НаправлениеФункцияЗапись в шаблоне родителя
Родитель → ребёнокinput()[todo]="todo"
Ребёнок → родительoutput()(toggle)="toggleTodo($event)"

Данные идут вниз, события — вверх. Это главное правило связи компонентов.

Частая ошибка

Называть выход как встроенное событие браузера, например click или change. На теге компонента сработают оба события: ваше и всплывшее из кнопки внутри, — и обработчик вызовется дважды, причём один раз с объектом MouseEvent. Давайте выходам говорящие имена: toggle, remove, saved.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.