Angular’s reactivity model has traditionally relied heavily on RxJS, but with the introduction of Signals in Angular 16+, the framework has entered a new phase. Signals are a fresh and powerful way to manage reactive state without the boilerplate and learning curve of observables. Let’s dive into what Angular Signals are, how they work, and why you should care.
What are Signals?
A Signal is a wrapper around a value that can notify subscribers when that value changes. Think of it as a reactive variable—when its value is updated, any computation or component that depends on it is automatically updated.
At their core, Signals provide:
- Automatic dependency tracking
- Push-based updates
- Synchronous reads
- Fine-grained reactivity
They aim to simplify state management and reduce the cognitive load compared to more complex patterns like BehaviorSubject
and manual subscription handling.
Why Signals?
Here’s why Angular introduced Signals:
- Simplicity: Easy to understand and use, even for beginners.
- Performance: Fine-grained reactivity enables fewer unnecessary change detections.
- Integration: Works seamlessly with the Angular rendering engine.
- Type Safety & Tooling: Fully typed and IDE-friendly.
Getting Started with Signals
You can start using signals by importing them from @angular/core
.
import { signal, computed, effect } from '@angular/core';
Example: A Counter with Signals
import { Component, signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<h2>Counter: {{ count() }}</h2>
<button (click)="increment()">Increment</button>
`
})
export class CounterComponent {
count = signal(0);
double = computed(() => this.count() * 2);
constructor() {
effect(() => {
console.log('Count changed:', this.count());
});
}
increment() {
this.count.update(value => value + 1);
}
}
Here’s what’s happening:
signal(0)
creates a reactive signal.count()
is how you read the signal value.count.update(...)
is how you write to it.computed()
defines a derived reactive value.effect()
is a reactive side-effect that re-runs when dependencies change.
Signals vs Observables
Feature | Signals | Observables (RxJS) |
---|---|---|
Push model | ✅ Yes | ✅ Yes |
Pull/synchronous read | ✅ Yes (count() ) | ❌ No (subscribe() only) |
Cleanup mechanism | ✅ With effect teardown | ✅ With unsubscribe |
Learning curve | 🟢 Low | 🔴 Moderate-high |
Operators | ⚠️ Limited (for now) | ✅ Rich (map, switchMap…) |
Debugging | ✅ Easier | ⚠️ Harder in complex chains |
When to Use Signals
Use Signals when:
- You want reactive local state in a component.
- You need derived state (
computed
) or reactive side-effects (effect
). - You want a simpler alternative to
BehaviorSubject
orNgRx
for small apps.
Stick with RxJS when:
- You’re dealing with async streams, like HTTP requests or WebSocket data.
- You need complex stream transformations with operators like
mergeMap
,debounceTime
, etc.
Testing with Signals
Testing signals is straightforward since they’re synchronous:
it('should increment the count', () => {
const count = signal(0);
count.update(v => v + 1);
expect(count()).toBe(1);
});
No need to worry about fakeAsync
, zones, or subscription cleanup!
Future of Signals in Angular
Signals are a core part of Angular’s reactivity roadmap. Upcoming improvements include:
- Signal-based forms
- Better dev tools support
- Signal-aware change detection
- Integration with the Angular Control Flow syntax (
@if
,@for
)
As Angular continues to modernize, Signals will become a key tool for cleaner, faster, and more maintainable apps.
Leave a Reply