You've probably heard about Signals if you're learning angular. At first, they can seem like just another state management feature, but they're actually one of the best additions to Angular.
In this article, we'll build a simple counter app to learn four core Signal APIs:
signal()update()computed()effect()
What is a Signal?
Think of a Signal as a reactive variable that Angular keeps an eye on.
With a normal variable, changing its value doesn't automatically tell Angular that something has changed.
A Signal is different. Angular knows exactly what has to be updated on the screen if its value changes.
Creating Your First Signal
import { signal } from '@angular/core';
count = signal(0);
Unlike a normal variable, you read a Signal by calling it like a function.
<h2>{{ count() }}</h2>
Notice the () at the end, that's how you access its current value.
Updating a Signal
There are two common ways to update a Signal.
Use set() when you want to replace the value completely.
count.set(10);
Use update() when the new value depends on the current one.
count.update(value => value + 1);
Let's add increment and decrement buttons.
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
<button (click)="decrement()">-</button>
<span>{{ count() }}</span>
<button (click)="increment()">+</button>
Every click updates the Signal, and Angular refreshes the UI automatically.
Deriving Values with computed()
Imagine you want to display whether the counter is even or odd.
Instead of calculating it every time, use computed().
import { computed } from '@angular/core';
isEven = computed(() => this.count() % 2 === 0);
Then display it in the template.
<p>{{ isEven() ? 'Even' : 'Odd' }}</p>
Whenever count changes, isEven updates automatically.
Use computed() whenever one value depends on another.
Reacting to Changes with effect()
Sometimes you don't want to calculate a value—you want to perform an action whenever a Signal changes.
That's what effect() is for.
import { effect } from '@angular/core';
constructor() {
effect(() => {
console.log('Count is', this.count());
});
}
Every time the counter changes, Angular runs the effect again.
Avoid using effect() to calculate values. That's what computed() is designed for.
Putting It All Together
import { Component, signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
templateUrl: './counter.component.html'
})
export class CounterComponent {
count = signal(0);
isEven = computed(() => this.count() % 2 === 0);
constructor() {
effect(() => {
console.log('Count changed:', this.count());
});
}
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
}
Signals make Angular state management much easier to understand. If you're just getting started, remember this simple rule:
-
Store state with
signal() -
Change state with
set()orupdate() -
Derive state with
computed() -
React to changes with
effect()
I hope this helps.
Top comments (0)