← Back to Chapters

Computed Signals in Angular

⚡Computed Signals in Angular

? Quick Overview

Computed signals in Angular allow you to create reactive values that automatically update whenever their dependent signals change. They are read-only and ensure that your derived state always stays in sync with the source signals. Whenever a source signal changes, Angular recomputes the computed signal and updates any bindings that use it.

? Key Concepts

  • Reactive derived state: Computed signals depend on one or more signals and always reflect the latest values.
  • Read-only values: You cannot directly set a computed signal; you only update its source signals.
  • Automatic recalculation: When any dependency changes, Angular recomputes the computed signal.
  • Cleaner templates: Move complex expressions from templates into computed signals for better readability.

? Syntax & Theory

A computed signal is created using the computed() function from @angular/core. Inside computed() you write a function that reads other signals. The return value of that function becomes the value of the computed signal.

Key points:

  • Use computed(() => ...) to describe how to derive a value.
  • Access other signals by calling them like functions, for example firstName().
  • Update only the base signals (such as firstName, price, tasks), never the computed ones.

? Basic Example: Full Name

? View Code Example (TypeScript)
// app.component.ts - using a computed signal for full name
import { Component, signal, computed } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
firstName = signal('John');
lastName = signal('Doe');

fullName = computed(() => this.firstName() + ' ' + this.lastName());
}
? View Code Example (Template)
<!-- app.component.html - template bound to the computed fullName -->
<p>First Name: {{ firstName() }}</p>
<p>Last Name: {{ lastName() }}</p>
<p>Full Name: {{ fullName() }}</p>

<button (click)="firstName.set('Jane')">Change First Name</button>

? Live Output / Explanation

Initially, the UI shows:

  • First Name: John
  • Last Name: Doe
  • Full Name: John Doe

When you click Change First Name, the firstName signal becomes 'Jane'. Angular automatically recomputes fullName to 'Jane Doe', and the template updates without any manual DOM handling.

? Computed Signals with Numbers

? View Code Example (TypeScript)
// app.component.ts - computing the total price from price and quantity
import { Component, signal, computed } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
price = signal(100);
quantity = signal(2);

total = computed(() => this.price() * this.quantity());
}
? View Code Example (Template)
<!-- app.component.html - showing the total and updating quantity -->
<p>Price: {{ price() }}</p>
<p>Quantity: {{ quantity() }}</p>
<p>Total: {{ total() }}</p>

<button (click)="quantity.set(quantity() + 1)">Increase Quantity</button>

? Live Output / Explanation

At the start, price() is 100 and quantity() is 2, so the computed signal total() becomes 200.

Each click on Increase Quantity calls quantity.set(quantity() + 1). The total computed signal automatically recalculates (for example: 3 × 100 = 300, 4 × 100 = 400, and so on) and the template updates accordingly.

? Nested Computed Signals (Task Lists)

? View Code Example (TypeScript)
// app.component.ts - splitting tasks into completed and remaining using computed signals
import { Component, signal, computed } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html'
})
export class AppComponent {
tasks = signal([
{ name: 'Learn Angular', done: true },
{ name: 'Build Project', done: false }
]);

completedTasks = computed(() => this.tasks().filter(t => t.done));
remainingTasks = computed(() => this.tasks().filter(t => !t.done));
}
? View Code Example (Template)
<!-- app.component.html - using Angular control flow syntax with computed task lists -->
<h3>Completed</h3>
@for (task of completedTasks(); track task) {
  <p>✅ {{ task.name }}</p>
}

<h3>Remaining</h3>
@for (task of remainingTasks(); track task) {
  <p>❌ {{ task.name }}</p>
}

? Live Output / Explanation

The tasks signal holds an array of task objects. Two computed signals are created:

  • completedTasks → tasks where done is true.
  • remainingTasks → tasks where done is false.

Whenever the tasks array changes (for example, when you mark a task as done or add a new one), both computed signals recompute, and the UI shows updated lists of completed and remaining tasks.

? Common Use Cases

  • Combining multiple simple signals into a formatted string (like a full name or address).
  • Calculating totals, discounts, or derived values in a shopping cart.
  • Filtering and grouping data (completed vs remaining tasks, active vs archived items, etc.).
  • Deriving UI state (for example, isFormValid computed from multiple field signals).

✅ Tips & Best Practices

  • Use computed signals to keep derived state in sync without manual recalculation.
  • Since computed signals are read-only, always update the underlying signals (such as price, quantity, or tasks), not the computed ones.
  • Move complex template expressions into computed signals to simplify your HTML and make it easier to test.
  • Be careful with very expensive computations; if needed, cache or optimise the logic so recomputation remains fast.
  • Keep your computed functions pure: avoid side effects and depend only on signals.

? Try It Yourself

  • Create two signals, firstName and lastName, and a computed signal fullName. Add buttons to change each name and observe how fullName updates.
  • Build a shopping cart where a computed signal calculates the total price from an array of items (each with price and quantity).
  • Use a tasks signal with several items, then add computed signals for completed, remaining, and total counts of tasks.
  • Define a computed signal like isCheckoutDisabled that depends on form field signals (for example, address, payment method) and use it to enable/disable a button.