Talking Between Components (@Input, @Output, ng-content)
A parent passes data down with @Input, a child sends events up with @Output, and ng-content lets a component wrap whatever you put inside it.
What you will learn
- Pass data into a child with @Input
- Send events out of a child with @Output and EventEmitter
- Wrap arbitrary content with ng-content
Why components must talk
Real apps are made of many components — a parent screen holding several smaller children. They constantly need to share: a parent gives a child the data to show, and a child tells the parent when something happens (a button was clicked, an item was deleted). Angular has three tools for this conversation.
- @Input — the parent passes data down into a child.
- @Output — the child sends an event up to the parent.
- ng-content — the parent puts HTML inside a child, which the child slots into place.
@Input: data flows down
Mark a child property with @Input and the parent can set it like an HTML attribute. Here a task-card child receives a title from its parent.
// task-card.component.ts (the child)
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-task-card',
standalone: true,
template: '<div class="card">{{ title }}</div>'
})
export class TaskCardComponent {
@Input() title = ''; // the parent fills this in
}Note: Output:
(No visible output by itself — this defines an input slot.)
@Input() title tells Angular “the parent may set this”. The card will show whatever title the parent passes in.
Now the parent uses the child and binds a value into that input with square brackets:
<!-- parent template -->
<app-task-card [title]="'Buy milk'"></app-task-card>
<app-task-card [title]="firstTask"></app-task-card>Note: Output:
Buy milk
[whatever firstTask holds]
[title]="..." is property binding into the child’s @Input. The first card gets a fixed string; the second gets the parent’s firstTask value.
@Output: events flow up
When the child needs to tell the parent something, it uses @Output with an EventEmitter — a little messenger that “emits” a value the parent can listen for.
// task-card.component.ts (the child)
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-task-card',
standalone: true,
template: '<button (click)="remove.emit(title)">Delete {{ title }}</button>'
})
export class TaskCardComponent {
@Input() title = '';
@Output() remove = new EventEmitter<string>(); // sends a string up
}Note: Output:
Delete Buy milk
Clicking the button calls remove.emit(title), which sends the title up to whoever is listening. The child does not delete anything itself — it just reports the click.
The parent listens for that event just like a normal event, with round brackets, and reacts:
<!-- parent template -->
<app-task-card [title]="'Buy milk'"
(remove)="deleteTask($event)">
</app-task-card>Note: Output:
(When Delete is clicked, the parent’s deleteTask('Buy milk') runs.)
(remove)="deleteTask($event)" listens for the child’s event; $event is the value the child emitted (the title). The parent decides what to do with it.
ng-content: wrap any HTML
Sometimes a component is a wrapper — a card, a panel, a dialog — and you want the parent to decide what goes inside. ng-content is a slot that says “put whatever the parent placed between my tags right here”.
<!-- panel.component.html (the child) -->
<div class="panel">
<ng-content></ng-content>
</div><!-- parent using the panel -->
<app-panel>
<h2>Welcome</h2>
<p>Anything here is projected into the panel.</p>
</app-panel>Note: Output:
Welcome
Anything here is projected into the panel.
The heading and paragraph the parent wrote appear inside the panel’s <div class="panel">, exactly where <ng-content> sits. This is called content projection.
| Tool | Direction | Use it for |
|---|---|---|
@Input() | Parent → child | Passing data down to a child |
@Output() + EventEmitter | Child → parent | Telling the parent something happened |
<ng-content> | Parent fills child | Reusable wrappers (cards, panels) |
Watch out: A child should not reach up and change its parent’s data directly. It emits an event and lets the parent decide what to do — this keeps data flow easy to follow (down with @Input, up with @Output).
Tip: This down-with-@Input, up-with-@Output pattern is the backbone of component design. Master it and you can break any screen into small, reusable, testable pieces.
Q. A child component needs to tell its parent that a button was clicked. Which tool does it use?
✍️ Practice
- Build a
rating-starschild with an@Input() valueand show it from a parent. - Add an
@Output() changedthat emits the new rating when a star is clicked.
🏠 Homework
- Make a reusable
cardcomponent that uses ng-content for its body and an @Input for its title, then use it twice with different content.