Services & RoutingExtra· 40 min read

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.

A child exposes title as an @Input
// 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:

The parent passes data into the child
<!-- 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.

The child emits a remove event
// 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:

The parent listens for the child’s output
<!-- 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”.

ng-content marks where projected content lands
<!-- panel.component.html (the child) -->
<div class="panel">
  <ng-content></ng-content>
</div>
The parent puts HTML inside the component
<!-- 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.

ToolDirectionUse it for
@Input()Parent → childPassing data down to a child
@Output() + EventEmitterChild → parentTelling the parent something happened
<ng-content>Parent fills childReusable 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?

Answer: @Output paired with an EventEmitter sends an event (and optional value) up to the parent, which listens for it with (eventName)="...".

✍️ Practice

  1. Build a rating-stars child with an @Input() value and show it from a parent.
  2. Add an @Output() changed that emits the new rating when a star is clicked.

🏠 Homework

  1. Make a reusable card component that uses ng-content for its body and an @Input for its title, then use it twice with different content.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →