Chapter 4: Outputs & Events

Outputs & Events!

This is where the child component gets to talk back to the parent. Up until now, data only flowed down (parent → child via inputs). Now we’ll make it flow up too (child → parent via outputs/events).

Think of it like this:

  • Inputs = “Hey child, here’s your name and age”
  • Outputs = “Hey parent, the user just clicked ‘Delete me!’ — do something about it!”

In modern Angular (v17+ → 19–21 as of 2026), we use the signal-based output() function instead of the old @Output() + EventEmitter. It’s cleaner, more type-safe, doesn’t require instantiating anything, and integrates nicely with the rest of the signals world.

We’ll cover two main things:

  1. Emitting custom events with output()
  2. Two-way binding using model() + the famous “banana-in-a-box” syntax [( )]

1. Emitting Events with output()

output() creates an emitter that the child uses to notify the parent when something interesting happens (click, change, delete, save, etc.).

Key points

  • Returns an OutputEmitterRef<T> (you call .emit(value) on it)
  • No need to create new EventEmitter<>() anymore — huge win!
  • Optional: alias if you want a different event name in templates
  • Events do not bubble up the DOM (unlike native click)
  • Parent listens with (eventName)=”handler($event)”

Example: Let’s upgrade our UserCard to emit events

We’ll add two outputs:

  • delete when user clicks a delete button
  • favoriteToggled when they toggle a favorite star

src/app/user-card/user-card.component.ts (add to existing)

TypeScript

Now in parent (app.component.ts)

TypeScript

→ Click “Delete User” → parent logs it and shows “Last deleted: Amit Sharma” → Toggle favorite → parent updates the message instantly

No more EventEmitter boilerplate — just output<…>() and .emit().

2. Two-Way Binding with model() + Banana-in-a-Box [( )]

model() is a special input that automatically creates a paired output named xxxChange (e.g. count → countChange).

This enables the classic [(ngModel)]-style two-way binding, but signal-powered and custom to your component.

When to use it?

  • Form-like controls: sliders, toggles, color pickers, editable text, counters
  • Parent wants to both set initial value and get updates when child changes it

Example: A simple Counter component with two-way binding

Generate it:

Bash

src/app/counter/counter.component.ts

TypeScript

Parent usage (app.component.ts)

TypeScript
TypeScript

What happens?

  • Parent sets initial 5 → child shows 5
  • Click + in child → count becomes 6 → parent’s myCount auto-updates to 6
  • Click Reset in parent → child instantly shows 0
  • Full two-way sync, powered by signals — no manual event handling needed!

Banana-in-a-box = [(count)] is sugar for:

HTML

But model() makes it automatic and reactive.

Quick Rules of Thumb

  • Use output<T>() for one-way notifications (delete, selected, saved)
  • Use model<T>() when you want two-way sync (value + onValueChange)
  • Always .emit() or .update()/set() on model to notify parent
  • model.required<T>() if value must be provided
  • You can alias: model(…, { alias: ‘value’ }) → [(value)]

Mini Practice Task

  1. Add a toggleFavorite output to UserCard (like above)
  2. Create a new component: app-like-button with liked = model<boolean>(false);
  3. Template: heart icon that toggles on click, updates liked
  4. Use it in app.component: <app-like-button [(liked)]=”articleLiked” />
  5. Show “Article is {{ articleLiked() ? ‘liked ❤️’ : ‘not liked ☹️’ }}”

Play with it — change in one place, see sync everywhere.

You’ve now got downward data flow (inputs), upward events (outputs), and two-way sync (model). Components are talking to each other like real team members!

Next chapter is usually services + dependency injection (shared state, API calls). Want to go there, or deepen outputs/model (more examples, alias/transforms, common gotchas)? Just say! 😄

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *