Input and Output Properties
Passing data down into a child component and sending events back up with @Input and @Output.
2 min de lecture
Components rarely work alone. A parent needs to pass data down to a child, and a child needs a way to tell the parent something happened. Angular's @Input and @Output decorators are how that conversation happens across a component boundary.
Passing data down with @Input
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-task-item',
template: `
<li [class.done]="task.completed">{{ task.title }}</li>
`,
})
export class TaskItemComponent {
@Input({ required: true }) task!: { id: number; title: string; completed: boolean };
}<!-- parent template -->
<app-task-item [task]="currentTask" />@Input() marks task as a property the parent is expected to set through a property binding — from the child's perspective, it's just a normal class property that happens to arrive from outside. Marking it required: true tells Angular's compiler to raise an error if some parent forgets to bind it, catching the mistake before the app ever runs rather than failing quietly at runtime with task as undefined.
Sending events up with @Output
import { Component, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-task-item',
template: `
<li>
{{ task.title }}
<button (click)="toggle()">Toggle</button>
</li>
`,
})
export class TaskItemComponent {
@Input({ required: true }) task!: { id: number; completed: boolean };
@Output() completedChange = new EventEmitter<boolean>();
toggle(): void {
this.task.completed = !this.task.completed;
this.completedChange.emit(this.task.completed);
}
}<!-- parent template -->
<app-task-item [task]="currentTask" (completedChange)="onTaskToggled($event)" />@Output() marks completedChange as an EventEmitter the child can call .emit() on. From the parent's template, it looks exactly like listening to a native DOM event — (completedChange)="onTaskToggled($event)" — because that's exactly what event binding was designed to support, whether the source is a browser event or a component's own output.
Why not just share a variable?
It would be simpler, in theory, for a child to reach up and mutate a parent's state directly, or for two sibling components to share a mutable object and edit it from either side. Angular deliberately doesn't make that easy: @Input/@Output keeps data flowing in one predictable direction — down through inputs, up through outputs — which makes it possible to look at a component in isolation and know exactly what can affect it (its inputs) and exactly what it can affect outside itself (its outputs), without tracing through the rest of the app.
When components need to share more than a pair of values
@Input/@Output works well for direct parent-child relationships. Once two components don't have a direct relationship — siblings, or components several levels apart — passing values through every intermediate layer gets unwieldy fast. That's usually the point to reach for a shared service instead, covered in the next section of this course.