Transform your Angular components using the cutting-edge Reactive Signal Inputs.
Kevin Kreuzer
@nivekcode
Jan 24, 2024
6 min read
If you’ve spent any time building Angular applications, you may have secretly hoped for a more reactive approach to managing component inputs — something that would truly liven up the workflow.
Well, that wish has come true. Angular now offers Signal Inputs, and they’re nothing short of a breakthrough.
Why Reactive Inputs?
To understand the value of reactive inputs, let’s first sketch a use case where they prove indispensable. Suppose we have an isEven component whose job is to accept a number and tell us whether it is even. Straightforward enough.
When building this isEven component in Angular, we face two conventional choices: leveraging a setter or relying on ngOnChanges. Initially, we might implement it with a decorator @Input paired with a setter.
@Component({
standalone: true,
selector: 'is-even',
template: `<h1>Is Even: {{ isEven }}</h1>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IsEvenComponent {
isEven: boolean | undefined;
@Input({required: true}) set counter(c: number){
this.isEven = c % 2 === 0;
};
}
With the isEven component fully wired up, we can now embed it directly within our template like this:
<is-even [counter]="5"/>
The setter method is now behind us—great work. Let’s turn our attention to the ngOnChanges lifecycle hook next.
@Component({
standalone: true,
selector: 'is-even',
template: `<h1>Is Even: {{ isEven }}</h1>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IsEvenComponent implements OnChanges {
isEven: boolean | undefined;
@Input({required: true}) counter!: number;
ngOnChanges(changes: SimpleChanges): void {
if(changes['counter']){
this.isEven = changes['counter'].currentValue % 2 === 0;
}
}
}
In Angular, both the setter and ngOnChanges represent valid solutions for responding to input changes, but they fundamentally rely on an imperative coding model.
Let’s shift focus now to how Input Signals enable a more declarative approach for handling this scenario.
Input Signals & Computed for the Win 🏅
With Input Signals, a fresh API (myInput = input<number>()) is introduced that delivers each Input as a Signal. 🌟🚀
@Component({
standalone: true,
selector: 'is-even',
template: `<h1>Is Even: {{ isEven() }}</h1>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IsEvenComponent {
counter = input.required<number>();
isEven = computed(() => this.counter() % 2 === 0);
}
Input Signals mark a shift toward a more elegant, entirely reactive paradigm 😍.
Angular is paving the way for a future where the ngOnChanges lifecycle hook is obsolete. Thanks to Input Signals, computed Signals and effects provide everything necessary to react to Input changes without extra ceremony.
Transform instead of Computed?
Although computed properties present a polished approach, the transform property can deliver an equivalent outcome—let’s examine it.
function isCounterEven(x: number): number {
return x % 2 === 0;
}
@Component({
standalone: true,
selector: 'is-even',
template: `<h1>Is Even: {{ isEven() }}</h1>`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class IsEvenComponent {
isEven = input.required<number, number>({
alias: 'counter',
transform: isCounterEven
});
}
While the transform function appears to be a reasonable solution, I would caution against it. Transform only handles a single operation. Suppose we need to compute several different values from the counter Input?
I came across this discussion on X where Alex Rickabaugh from the Angular team shared a useful tip that I would phrase in my own wording as follows:
Use transforms for adjustments, not for reshaping. They work well for minor parsing or type coercion, but let's avoid altering the fundamental purpose of our Input.

Required and Optional Inputs
Up until now, our discussion has centered mainly on required Inputs. Naturally, though, not every Input has to be mandatory. Optional Inputs can also be built using Signals.
// Optional Input property with undefined as initial value
counter = input<number>();
// Optional Input property with initial value
counter = input(0);
// Required Input - does not have a initial value
counter = input.required<number>();
Input Aliasing
Signal Inputs extend the aliasing capability found in decorator-based Inputs, so you can assign distinct external names. This option is supplied through an options object passed into the constructor.
@Component({
selector: 'user',
standalone: true,
template: `{{ customer() }}`,
})
export class UserProfile {
customer = input<Customer>({ alias: 'user' });
}
Route Params as Signal
In current Angular versions, there's a way to expose route parameters directly as Inputs. This handy capability becomes available by adding the withComponentInputBinding option to the router configuration.
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withComponentInputBinding())]
};
When you turn on the withComponentInputBinding option in the Router, route parameters not only become available as standard Input properties but also smoothly convert into Input Signals. 🌟
@Component({
standalone: true,
selector: 'todo-item',
template: ``,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoItemComponent {
id = input.required<number>();
}
Fetching Data as Side Effect
Input Signals unlock a direct path for running side effects from their values. Let’s take the same component again and output the Id taken from the route param using an effect.
@Component({
standalone: true,
selector: 'todo-item',
template: ``,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoItemComponent {
id = input.required<number>();
constructor(){
effect(() => console.log(this.id());
}
}
That’s a neat trick, though merely logging an idea isn’t exactly a practical or polished use case. What if we instead pull the TodoItem from a remote server using a TodosService?
@Component({
standalone: true,
selector: 'todo-item',
template: `{{ todo() | json }}`,
imports: [JsonPipe]
})
export default class TodoItemComponent {
private todoService = inject(TodosService);
id = input.required<string>();
todo = signal(null);
constructor() {
effect(() => {
this.todoService.getTodo(this.id())
.subscribe((t) => this.todo.set(t));
});
}
}
For this example to function, we need to generate a dedicated todo Signal.
Calling the Signal constructor yields a
WritableSignal. Through thisWritableSignal, the Signal's value can be modified using the set or update functions.
Afterward, we invoke the subscribe method with a callback function to retrieve the Todo and assign it to the todo Signal.
While this method is functional, it maintains an imperative feel. In such situations, switching to the toObservable function proves more beneficial, as it transforms the id Signal into an Observable.
After converting our Signal to an Observable, we apply switchMap to shift to an Observable stream of the Todo, which is generated from a backend call within the TodosService. Finally, we employ toSignal once more to revert the stream back into a Signal.
@Component({
standalone: true,
selector: 'todo-item',
template: `{{ todo() | json }}`,
imports: [JsonPipe]
})
export default class TodoItemComponent {
private todoService = inject(TodosService);
id = input.required<string>();
todo = toSignal(
toObservable(this.id)
.pipe(
switchMap((i) => this.todoService.getTodo(i)
)
)
);
}
Excellent — and as it happens, there’s a third alternative tailored to exactly this situation. The ngxtension-platform library offers its own computedAsync utility, which fits our case perfectly.
@Component({
standalone: true,
selector: 'todo-item',
template: `{{ todo() | json }}`,
imports: [JsonPipe]
})
export default class TodoItemComponent {
private todoService = inject(TodosService);
id = input.required<string>();
todo = computedAsync(
() => this.todoService.getTodo(this.id())
)
}
For my own part, I find computedAsync quite appealing — it’s a tidy approach. That said, adopting it across an entire project means committing to a third-party dependency. Given that Angular is under constant development, any external library must keep pace. As is often the case, choosing between cutting-edge solutions and minimizing external reliance is a trade-off.
Wrapping Up
Angular Signals represent more than a mere addition; they mark a significant move towards a signal-driven component model.
With these Signals, a wide range of options emerges, letting us completely incorporate the signal pattern into our Angular applications. This evolution points to a future where reactivity and clarity take center stage in component architecture, offering a more user-friendly and streamlined development workflow.
Indeed, Angular Signals stand as a landmark of innovation, leading us toward a livelier and more adaptable web development ecosystem.
Enjoying how the code samples look? Take a look at our new theme plugin
Skol - the ultimate IDE theme
Bring the aurora borealis experience right into your editor. A minimalist yet powerful dark theme that is both easy on the eyes and aesthetically pleasing.
Leverage AI in your Angular development workflow
Angular + AI Video Course
This practical course demonstrates how to bring AI into Angular applications by leveraging Hash Brown for crafting smart, responsive interfaces.
Explore real-time chat, tool invocation, generative UI, structured outputs, and other topics progressively.
Get ready for what’s next in Angular and level up to Angular Signals mastery now!
Angular Signals Masterclass eBook
Explore why Angular Signals are indispensable, get acquainted with their flexible API, and open up the secrets behind how they work internally.
Take your coding skills to the next level and gear up for what’s coming in Angular. Stay ahead of the curve!
Enjoying the read and ready to become an expert in Angular’s cutting-edge Signal Forms?
Angular Signal Forms: A Practical Deep Dive
Twelve progressive chapters combine theoretical insights with practical exercises to help you master Angular's fresh Signal-Forms.
Explore form fundamentals, validation rules, bespoke controls, nested form structures, transition tactics, and beyond!
Stay in the loop
on fresh articles
Subscribe to the Angular Experts Content Updates & News feed, and we'll let you know the moment new posts about Angular, Ngrx, RxJs, or other exciting Frontend subjects go live!
Your email stays private with us, and unsubscribing is always an option!
Your feedback & questions
Feel free to inquire about anything and share your personal insights or viewpoint on the matter
You might also like
Browse these guides from Angular Experts to deepen your knowledge on adjacent subjects such as Angular !

Top 10 Angular Architecture Mistakes You Really Want To Avoid
In 2024, Angular keeps changing for better with ever increasing pace, but the big picture remains the same which makes architecture know-how timeless and well worth your time!

Tomas Trajan
@tomastrajan
Sep 10, 2024
15 min read

Improving DX with new Angular @Input Value Transform
Embrace the Future: Moving Beyond Getters and Setters! Learn how to leverage the power of custom transformers or the build in booleanAttribute and numberAttribute transformers.

Kevin Kreuzer
@nivekcode
Nov 18, 2023
3 min read

Angular Material components testing
How and why to use Angular Materials component harness to write reliable, stable and readable component tests

Kevin Kreuzer
@nivekcode
Feb 14, 2023
7 min read
Put our deep expertise to work for your team
For years, Angular Experts has partnered with both enterprises and early-stage companies, delivering workshops, crafting tutorials, and building a broad set of open source tools. Our hands-on knowledge of the modern front-end landscape is something we are proud of, and we would be delighted to support your growth
