Post

Modern Angular 06: Computed Signals

Modern Angular 06: Computed Signals

This is lesson 6 of the Modern Angular Course. In the previous lesson, we introduced writable signals and used them to manage local component state. Now we build on that foundation with computed signals — derived state that updates automatically when its dependencies change.

This post is part of the Modern Angular Course series. Check the course page for the full episode list.

In this post, we cover:

  • Why derived state matters and the problems it solves
  • What a computed signal is and how it works
  • How to create and use computed signals in templates
  • How computed signals compare to methods
  • Common mistakes to avoid

Why Derived State Matters

In many components, we do not just store state — we also derive values from that state. Examples:

  • A label based on a counter
  • A button enabled or disabled based on a condition
  • A filtered list based on user input

Without computed signals, this logic often ends up duplicated, spread across the component, or recalculated manually. Computed signals exist to solve this.

What Is a Computed Signal?

A computed signal represents derived state. It:

  • Depends on one or more signals
  • Recalculates automatically when its dependencies change
  • Is read-only by design — you do not set its value directly

Angular tracks which signals a computed signal reads and keeps it up to date for you. No manual wiring, no extra code.

Creating Your First Computed Signal

Let’s extend our counter example from the previous lesson. First, import computed from @angular/core:

1
import { Component, computed, signal } from '@angular/core';

Now define a computed signal that derives from count:

1
2
3
protected count = signal(0);

protected doubleCount = computed(() => this.count() * 2);

Here is what is happening: doubleCount reads count() inside its computation function. Angular tracks this dependency automatically. Whenever count changes, doubleCount recalculates and any template bindings that read doubleCount() update.

Using Computed Signals in Templates

Using a computed signal in the template looks exactly like using a regular signal — you call it with parentheses:

1
2
<h1>Count: {{ count() }}</h1>
<h1>Double: {{ doubleCount() }}</h1>

When count changes:

  1. The computed signal recalculates
  2. The UI updates
  3. Nothing else needs to change

The template stays declarative. The component owns the logic.

Computed Signals vs Methods

You might wonder: “Why not just use a method instead?”

1
2
3
getDoubleCount() {
  return this.count() * 2;
}

This works, but there is an important difference. Methods:

  • Run every time change detection runs — even if the underlying data has not changed
  • Do not express dependencies explicitly
  • Can become expensive as logic grows

Computed signals:

  • Run only when their dependencies change — Angular caches the result
  • Clearly express data relationships
  • Are easier to reason about and test

For a simple multiplication, the performance difference is negligible. But as computations become more complex — filtering lists, formatting data, combining multiple signals — computed signals avoid unnecessary work and make the code more predictable.

The Complete Component

Here is the full component with the computed signal added:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { Component, computed, signal } from '@angular/core';

@Component({
  selector: 'app-hello',
  imports: [],
  templateUrl: './hello.html',
  styleUrl: './hello.scss',
})
export class Hello {

  protected title = 'Welcome to Modern Angular!';

  protected isDisabled = false;

  protected onClick() {
    console.log('Button clicked');
    this.isDisabled = !this.isDisabled;
  }

  protected count = signal(0);

  protected doubleCount = computed(() => this.count() * 2);

  protected increaseCounter() {
    this.count.update(value => value + 1);
  }

  protected decreaseCounter() {
    this.count.update(value => value - 1);
  }

  protected resetCounter() {
    this.count.set(0);
  }
}

And the full template:

1
2
3
4
5
6
7
8
9
10
11
12
13
<p>This is my first Angular component!</p>

<h1>{{ title }}</h1>

<button [disabled]="isDisabled"
  (click)="onClick()">Toggle</button>

<h1>Count: {{ count() }}</h1>
<h1>Double: {{ doubleCount() }}</h1>

<button (click)="increaseCounter()">+</button>
<button (click)="decreaseCounter()">-</button>
<button (click)="resetCounter()">Reset</button>

Click the increment or decrement buttons, and both count and doubleCount update in the UI. The computed signal stays in sync automatically.

Common Mistakes with Computed Signals

A few things to keep in mind:

  • Do not try to update a computed signal directly — it is read-only. Use signal() for state you need to change.
  • Avoid side effects inside computed — computed signals should only derive values, not trigger actions like HTTP calls or logging.
  • Use computed signals only for derived values — if you need to react to signal changes and perform side effects, that is where effects come in.

Source Code

The full source code for the course is available on GitHub: loiane/modern-angular.

Next Step

In the next lesson, we introduce effects — the third piece of the signals trio. We will see how to react to signal changes, perform side effects safely, and keep state and effects separate. That completes the core signals model: writable signals, computed signals, and effects.

Watch the Video

This post is part of the Modern Angular Course series. Check the course page for the full episode list.

This post is licensed under CC BY 4.0 by the author.
This site uses cookies. Please choose whether to accept analytics cookies. Privacy Policy