Inputs and Outputs: Parent-Child Communication
Passing data into a child component and emitting events back up to a parent.
What you'll learn
- Declare an @Input() property to receive data from a parent component
- Declare an @Output() EventEmitter to send data/events back to a parent
- Explain the one-directional data flow this pattern encourages
Explanation
A child component receives data from its parent via an @Input() property: @Input() title: string = ""; -- the parent sets it via property binding when using the child: <app-card [title]="cardTitle"></app-card>.
A child sends data or events back up to its parent via an @Output() EventEmitter: @Output() selected = new EventEmitter<number>();, then calling this.selected.emit(this.id) inside the child fires the event, and the parent listens with event binding: <app-card (selected)="onCardSelected($event)"></app-card> -- $event inside the parent's template refers to whatever value the child's emit() call passed.
This @Input/@Output pattern encourages a clear, one-directional data flow: data flows down through inputs, events flow up through outputs -- rather than a child directly reaching up and mutating its parent's state, which would make data flow much harder to trace through a larger app.
Guided lab
Predict: An @Output EventEmitter's emitted value
This models an @Output EventEmitter's behavior with a plain callback, since a live parent-child template can't render here. Predict what the parent's handler receives.
class CardComponent {
id = 42;
private listeners: ((id: number) => void)[] = [];
onSelected(callback: (id: number) => void): void {
this.listeners.push(callback);
}
select(): void {
for (const listener of this.listeners) {
listener(this.id);
}
}
}
const card = new CardComponent();
card.onSelected((id) => console.log("Parent received id:", id));
card.select();Stuck? Get a hint.
Common mistakes
- Trying to have a child component directly mutate a property on its parent, instead of emitting an @Output() event and letting the parent decide how to respond.
- Forgetting to actually call .emit(value) inside the EventEmitter, then wondering why the parent's (event) handler never fires.
- Mismatching the type parameter on EventEmitter<T> with what's actually passed to emit(), losing type safety on the parent's $event.
Knowledge check
Takeaway
Pass data into a child with @Input(), and have the child notify its parent with an @Output() EventEmitter's .emit() call -- data down, events up.
Summary
@Input() properties receive data from a parent; @Output() EventEmitters, fired with .emit(), send events back up -- encouraging clear, one-directional data flow.
References
Your notes
Notes save automatically.
Finished this lesson?
Mark it complete to track your progress and schedule a future review.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.