Under The Hood
frontend angular Angular 17.1+ (input/output), 17.2+ (model)

input(), output(), and model() Signal Primitives

Last updated
Prerequisites:
Angular component basics (@Input/@Output)
Understanding of Angular signals
  • angular
  • signals
  • input
  • output
  • model
  • component-api

Read at your depth

The practical view

Angular components traditionally declare inputs with @Input() and outputs with @Output() + EventEmitter. The signal-based API replaces them: count = input(0); gives a signal input (read count() in templates/code), and changed = output<number>(); emits like an EventEmitter (this.changed.emit(5)). For two-way binding, value = model(0); creates a model input that also emits updates when you set it — [(value)] works with zero boilerplate. Signal inputs are read-only from the child's perspective (you cannot assign to count(); you can only read it). This API is the modern, type-safe replacement for decorator-based bindings.

The same idea in other frameworks

react equivalent
Props are inputs; model() resembles a controlled value + onChange pair bundled together.

Legacy vs modern

Decorator @Input/@Output vs signal input()/output()/model()

Decorators are strings (class-field based) with EventEmitter boilerplate; signal primitives are typed, reactive, and model() removes the emit boilerplate for two-way bindings.

before → after
Decorator inputs/outputs
@Input() count = 0; // ❌ OLD: string-keyed, non-reactive field
@Output() changed = new EventEmitter<number>();
@Output() countChange = new EventEmitter<number>(); // ❌ OLD: manual model plumbing
Signal inputs/outputs/model
count = input(0); // ✅ NEW: typed, reactive, read-only input
changed = output<number>();
countModel = model(0); // ✅ NEW: two-way binding in one primitive

Interview gotchas

Context

This tests whether you understand the reactivity model shift — that inputs as signals create granular dependencies rather than whole-component input-change triggers.

The mechanical answer

An @Input() is a plain field; when it changes, the framework marks the component dirty and re-checks it (and its template) as a unit. A signal input is a reactive node: template bindings that read count() register as consumers, so when the parent writes a new value, only those specific bindings re-evaluate — aligned with zoneless, granular change detection. Signal inputs are also read-only from the child: you cannot assign to an input() signal (the compiler enforces this), which prevents the anti-pattern of mutating inputs locally. The trade-off: you cannot use signal inputs with template reference variables the same way, and the value must be read with () or used inside reactive contexts; also, non-signal consumption (e.g. in ngOnInit) requires reading once and storing, losing reactivity.

Trap

The naive answer is 'signal inputs are just typed @Input'. They change the invalidation granularity and add read-only semantics. Another trap: trying to assign to an input signal inside the child or expecting changes to reflect when you read the value once and store it in a plain variable — you must read it in a reactive context (template, computed, effect). Candidates who mention consumer registration and read-only enforcement show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.