Skip to content
intermediate Phase 11 · Signals and Change Detection

Signal Inputs and Outputs

Declare reactive component APIs without decorator boilerplate.

40m
0 problems
Topic Progress 0%

Signal-Based Component APIs

Signal-based inputs and outputs provide reactive component APIs without decorator boilerplate.

input() — Reactive Input

import { Component, input } from '@angular/core';

@Component({
  selector: 'app-card',
  template: `
    <div class="card">
      <h3>{{ title() }}</h3>
      <p>{{ description() }}</p>
    </div>
  `,
})
export class CardComponent {
  title = input<string>('Untitled');
  description = input<string>('');
  variant = input<'primary' | 'secondary'>('primary');
}
<app-card [title]="cardTitle" [description]="cardDescription"></app-card>

input.required() — Mandatory Input

@Component({
  selector: 'app-alert',
  template: `<div class="alert">{{ message() }}</div>`,
})
export class AlertComponent {
  message = input.required<string>();
}

If a parent does not provide a required input, Angular throws a compile-time error.

output() — Typed Events

import { Component, output } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `<button (click)="selected.emit('user-123')">Select</button>`,
})
export class ChildComponent {
  selected = output<string>();
}
<app-child (selected)="onSelected($event)"></app-child>

model() — Two-Way Binding

import { Component, model } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <button (click)="count.update(c => c - 1)">-</button>
    <span>{{ count() }}</span>
    <button (click)="count.update(c => c + 1)">+</button>
  `,
})
export class CounterComponent {
  count = model<number>(0);
}
<app-counter [(count)]="parentCount"></app-counter>

This is equivalent to:

<app-counter [count]="parentCount" (countChange)="parentCount = $event"></app-counter>

Migrating from Decorators to Signals

Signal-based APIs can be adopted incrementally alongside existing @Input/@Output decorators.

Migration Comparison

Feature Decorator Signal
Input @Input() title: string title = input<string>()
Required input @Input({ required: true }) input.required<string>()
Output @Output() selected = new EventEmitter<string>() selected = output<string>()
Two-way @Input() count + @Output() countChange count = model<number>(0)
Alias @Input('alias') input({ alias: 'alias' })

Input with Transform

import { Component, input, booleanAttribute } from '@angular/core';

@Component({
  selector: 'app-toggle',
  template: `<span [class.active]="isActive()">Toggle</span>`,
})
export class ToggleComponent {
  isActive = input(false, { transform: booleanAttribute });
}
<app-toggle [isActive]="true"></app-toggle>
<app-toggle isActive></app-toggle>

Input with Alias

@Component({
  selector: 'app-user-avatar',
  template: `<img [src]="avatarUrl()" [alt]="altText()">`,
})
export class UserAvatarComponent {
  avatarUrl = input<string>(undefined, { alias: 'src' });
  altText = input<string>('Avatar', { alias: 'alt' });
}
<app-user-avatar [src]="user.imageUrl" [alt]="user.name"></app-user-avatar>

When to Use Signals vs Decorators

  • New components — prefer signal-based APIs
  • Existing components — @Input/@Output work fine; migrate incrementally
  • Library authors — signal APIs work in both standalone and NgModule contexts

Quiz

1. How do you declare a signal-based input?

Question 1 options

2. What does model() create?

Question 2 options

3. How do you make a signal input required?

Question 3 options

Flashcards

Question

How do you declare a signal input?

Answer

title = input<string>() — read with title().

Question

What does model() create?

Answer

A two-way bindable signal. Use [(syntax)] in parent template.

Question

How do you make an input required?

Answer

input.required<string>() — compile-time error if not provided.

Revision Notes

Key Takeaways

  • 1. input() declares reactive component inputs
  • 2. input.required() enforces mandatory inputs
  • 3. model() creates two-way bindable signals
  • 4. output() replaces EventEmitter for typed events

Interview Tips

  • • Explain the benefits of signal-based component APIs
  • • Be ready to describe model() for two-way binding
  • • Mention incremental migration from decorators

Cheat Sheet

Signal Inputs and Outputs Cheat Sheet

Input:

title = input<string>('Untitled');

Required input:

message = input.required<string>();

Output:

selected = output<string>();

Two-way binding:

count = model<number>(0);
<app-counter [(count)]="parentCount"></app-counter>

Continue Learning