Lesson 7 of 30
input() and input.required() turn component inputs into read-only signals — no more @Input() decorator, no more 'is this defined yet' guessing.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
The old way to receive data into an Angular component was @Input() title: string; — a decorator on a plain class property. It worked, but it had a real gap: the property existed the moment the class was constructed, before Angular had actually set the value from the parent's template binding, which is exactly why lifecycle hooks like ngOnChanges and ngOnInit had to exist — you needed a signal (in the everyday English sense) that told you "the inputs are now actually populated."
input() closes that gap by making the input itself a signal. title = input<string>() doesn't just declare a property — it gives you back a read-only signal function. You read the current value by calling title(), the same as any other signal. Angular sets its value when the parent binds to it, and because it's a signal, anything that depends on it (a computed(), an effect(), or just the template) reacts automatically, with no separate change-detection hook required to notice.
Inputs come in two flavors: input() for optional inputs (with an optional default value), and input.required() for inputs the parent must provide — a compile-time guarantee, not just a runtime check.
import { Component, input, computed } from '@angular/core';
@Component({
selector: 'app-product-card',
standalone: true,
template: `
<article class="card">
<h3>{{ name() }}</h3>
<p>{{ formattedPrice() }}</p>
@if (isOnSale()) {
<span class="badge">Sale</span>
}
</article>
`,
})
export class ProductCardComponent {
// required — the parent MUST bind this or it's a compile-time template error
name = input.required<string>();
// optional, with a default
priceCents = input<number>(0);
discountPercent = input<number>(0);
// derived state built straight from the input signals
isOnSale = computed(() => this.discountPercent() > 0);
formattedPrice = computed(() => {
const cents = this.priceCents() * (1 - this.discountPercent() / 100);
return `$${(cents / 100).toFixed(2)}`;
});
}
Used from a parent as <app-product-card [name]="p.name" [priceCents]="p.priceCents" [discountPercent]="p.discount" />. Notice formattedPrice and isOnSale are computed() values built directly on top of the input signals — when the parent rebinds discountPercent to a new value, isOnSale and formattedPrice recompute automatically, and only the DOM that actually depends on them updates. There's no ngOnChanges diffing SimpleChanges by hand to notice the value moved.
this.name.set(...) — Angular owns the value, the parent controls it via binding. If you need a locally-mutable copy, derive it into a real signal() in the constructor, or reach for model() if you actually want two-way binding.input.required() is a compile-time and template-type guarantee, not a runtime null check. If a parent template forgets to bind a required input, Angular's template type-checking catches it before the app even runs — you don't get a defensive if (!this.name()) at runtime because the type system already ruled that case out.ngOnInit just to react to an input arriving. Because inputs are signals, a computed() or effect() built on top of them just reacts naturally — reaching for a lifecycle hook to "wait until inputs are ready" is largely a decorator-era habit that signals make unnecessary.This is the same job React's props do — data flowing one direction, parent to child — but where a React prop is just a plain JavaScript value re-passed on every render, an Angular signal input is a stable reactive cell: dependents recompute automatically without the parent function needing to re-run at all. It builds directly on the signals-fundamentals lesson — an input is nothing more than a signal that Angular, rather than your own code, calls .set() on.