Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Outputs: output()

An output is a component's own event. The child does not change the parent's data itself; it reports: "the user checked the task". The parent listens to this event the same way it listens to click and decides what to do.

Declaring an output

The output() function creates an event. The angle brackets hold the type of data it carries:

// 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);
  }
}

The emit() method sends the event with the task id.

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

Clicking the checkbox calls onToggle(), which calls emit().

Listening in the parent

The parent writes the output name in parentheses. The value passed to emit() is available as $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)),
  );
}

Check the box — the task becomes completed, and the "Left to do" counter from the signals lesson goes down. The data was changed by its owner — the parent.

An event without data

If there is nothing to pass, leave out the type and call emit() without an argument. Let's add a delete button:

// src/app/todo-item/todo-item.ts
remove = output();
<!-- src/app/todo-item/todo-item.html -->
<button (click)="remove.emit()">Delete</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));
}

The parent knows the task id itself — it takes it from the todo variable of the @for loop.

Inputs and outputs together

DirectionFunctionIn the parent's template
Parent → childinput()[todo]="todo"
Child → parentoutput()(toggle)="toggleTodo($event)"

Data flows down, events flow up. This is the main rule of component communication.

Common mistake

Naming an output after a built-in browser event, such as click or change. Both events then fire on the component tag: yours and the one bubbling up from the button inside — and the handler runs twice, once with a MouseEvent object. Give outputs meaningful names: toggle, remove, saved.

Thanks for reading!

Donate to the site — we work hard for you.