LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 2215 min read

Signal-based Inputs & Model

Use the modern input(), output(), and model() functions as signal-based alternatives to @Input and @Output.

input()

The input() function creates a signal-based input, readable with the same () call syntax as any other signal — a modern alternative to the @Input() decorator.

import { Component, input } from '@angular/core';
@Component({
selector: 'app-product-card',
standalone: true,
template: `<h3>{{ title() }}</h3>`,
})
export class ProductCardComponent {
title = input(''); // input signal with a default value
}

Required and Transformed Inputs

export class ProductCardComponent {
title = input.required<string>();
price = input(0, {
transform: (value: string | number) => Number(value), // transform incoming values
});
}

output()

The output() function is the signal-era alternative to @Output() + EventEmitter, with a simpler API for emitting events.

import { Component, output } from '@angular/core';
@Component({
selector: 'app-like-button',
standalone: true,
template: `<button (click)="liked.emit()">👍 Like</button>`,
})
export class LikeButtonComponent {
liked = output<void>();
}

model() for Two-Way Binding

model() combines an input and its matching output into one declaration, directly enabling the [(...)] banana-in-a-box syntax on your own component with a single line, instead of pairing a separate input() and output().

import { Component, model } from '@angular/core';
@Component({
selector: 'app-stepper',
standalone: true,
template: `
<button (click)="value.set(value() - 1)">-</button>
{{ value() }}
<button (click)="value.set(value() + 1)">+</button>
`,
})
export class StepperComponent {
value = model(0);
}
<app-stepper [(value)]="quantity" />
One Declaration Instead of Two

Compare this to the earlier @Input()/@Output() two-way binding lesson, which required a separate value input and valueChange output — model() achieves the identical result in a single line.

FAQs

Signal-based input()/output()/model() are the modern recommendation for new code — they integrate directly with computed() and effect() without any extra conversion step.

Technically possible during migration, but pick one style per component for clarity — mixing both in the same class is confusing to read.

Summary

input(), output(), and model() bring signal-based reactivity directly to component communication, with model() notably collapsing two-way binding into a single declaration. Next, you'll look at how change detection itself works, both classically and with signals.

Next Lesson →

Change Detection & Zone.js