Emitting Events to Parent Components
The @Output decorator allows a child component to notify its parent of user actions or state changes by emitting events.
Basic Usage
// child.component.ts
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
template: `<button (click)="onSelect()">Select</button>`,
})
export class ChildComponent {
@Output() selected = new EventEmitter<string>();
onSelect(): void {
this.selected.emit('user-123');
}
}
// parent.component.ts
@Component({
selector: 'app-parent',
template: `
<app-child (selected)="onSelected($event)"></app-child>
<p>Selected: {{ selectedId }}</p>
`,
})
export class ParentComponent {
selectedId = '';
onSelected(id: string): void {
this.selectedId = id;
}
}
EventEmitter is an Observable
EventEmitter extends Subject, so you can use RxJS operators on it:
@Output() search = new EventEmitter<string>();
ngOnInit(): void {
this.search.pipe(
debounceTime(300),
distinctUntilChanged()
).subscribe(query => {
// perform search
});
}
Output Aliases
@Output('itemSelected') selected = new EventEmitter<Item>();
<app-child (itemSelected)="onSelect($event)"></app-child>
Emitting Multiple Values
@Output() action = new EventEmitter<{ type: string; payload: any }>();
this.action.emit({ type: 'delete', payload: { id: 123 } });
Signal-Based Outputs (Modern Angular)
Angular 17+ provides output() as a signal-based alternative to @Output.
output() Function
import { Component, output } from '@angular/core';
@Component({
selector: 'app-child',
template: `<button (click)="selected.emit('user-123')">Select</button>`,
})
export class ChildComponent {
selected = output<string>();
}
@Output vs output()
| Feature | @Output | output() |
|---|---|---|
| Syntax | Decorator + EventEmitter | Function call |
| Type safety | EventEmitter |
output |
| Alias | @Output('alias') | output({ alias: 'alias' }) |
| Operators | Yes (EventEmitter extends Subject) | No (use pipe in parent) |
model() for Two-Way Binding
model() creates a signal that supports two-way binding with [(syntax)]:
@Component({
selector: 'app-counter',
template: `
<button (click)="count.set(count() - 1)">-</button>
<span>{{ count() }}</span>
<button (click)="count.set(count() + 1)">+</button>
`,
})
export class CounterComponent {
count = model<number>(0);
}
<app-counter [(count)]="parentCount"></app-counter>
This is equivalent to:
<app-counter [count]="parentCount" (countChange)="parentCount = $event"></app-counter>
Complete Parent-Child Communication Example
// child.component.ts
@Component({
selector: 'app-todo-item',
template: `
<div [class.done]="item().done">
<input type="checkbox" [checked]="item().done" (change)="toggle.emit()">
<span>{{ item().text }}</span>
<button (click)="remove.emit()">Delete</button>
</div>
`,
})
export class TodoItemComponent {
item = input.required<{ text: string; done: boolean }>();
toggle = output<void>();
remove = output<void>();
}
// parent.component.ts
@Component({
selector: 'app-todo-list',
template: `
<app-todo-item
*ngFor="let todo of todos"
[item]="todo"
(toggle)="toggleTodo(todo)"
(remove)="removeTodo(todo)">
</app-todo-item>
`,
})
export class TodoListComponent {
todos = [
{ text: 'Learn Angular', done: false },
{ text: 'Build app', done: false },
];
toggleTodo(todo: any): void {
todo.done = !todo.done;
}
removeTodo(todo: any): void {
this.todos = this.todos.filter(t => t !== todo);
}
}
Quiz
1. What class does @Output use to emit events?
2. What is the signal-based alternative to @Output?
3. What does model() create?
Flashcards
Question
How does a child component notify its parent?
Click to reveal answer
Answer
@Output() with EventEmitter, or output() signal. Parent listens with (event) binding.
Question
What is EventEmitter?
Click to reveal answer
Answer
A class that extends Subject. The child calls .emit(value) and subscribers receive the value.
Question
What does model() do?
Click to reveal answer
Answer
Creates a two-way bindable signal. Use [(property)] syntax in the parent template.
Revision Notes
Key Takeaways
- 1. @Output + EventEmitter enables child-to-parent communication
- 2. output() is the modern signal-based alternative
- 3. model() creates two-way bindable signals
- 4. EventEmitter extends Subject, so RxJS operators work on it
Interview Tips
- • Explain the data flow: @Input down, @Output up
- • Be ready to describe model() for two-way binding
- • Mention that EventEmitter is an Observable under the hood
Cheat Sheet
@Output Cheat Sheet
Decorator syntax:
@Output() selected = new EventEmitter<string>();
this.selected.emit('value');
Signal syntax:
selected = output<string>();
this.selected.emit('value');
Two-way binding:
count = model<number>(0);
<app-counter [(count)]="parentCount"></app-counter>
Parent template:
<app-child (selected)="onSelected($event)"></app-child>