Back to all posts

Signals in Angular Change Detection


With Angular 16+, Signals introduce a fine-grained reactivity model that improves change detection by updating only the affected parts of the UI instead of triggering a full component tree check.

Why Use Signals for Change Detection?

Traditional Angular change detection relies on Zone.js to track changes, which can lead to unnecessary DOM updates. Signals offer a push-based approach, ensuring that only dependent components update when a signal value changes.

How Signals Work in Angular Change Detection

  • Signals hold reactive values that notify consumers when updated.
  • They automatically track dependencies, ensuring efficient updates.
  • They reduce reliance on ChangeDetectionStrategy.OnPush and manual optimizations like markForCheck().

Using Signals in Angular Components

Basic Example of Signals in a Component

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

@Component({
  selector: 'app-counter',
  template: `
    <h2>Counter: {{ count() }}</h2>
    <button (click)="increment()">Increment</button>
  `,
})
export class CounterComponent {
  count = signal(0); // Define a signal

  increment() {
    this.count.set(this.count() + 1); // Update signal value
  }
}

Here, count() acts like a reactive getter.

The template updates only when count changes, reducing unnecessary re-renders.

Computed Signals for Derived State

Computed signals let you create values that automatically update when dependencies change.

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

@Component({
  selector: 'app-counter',
  template: `
    <h2>Counter: {{ count() }}</h2>
    <h3>Double: {{ doubleCount() }}</h3>
    <button (click)="increment()">Increment</button>
  `,
})
export class CounterComponent {
  count = signal(0);
  doubleCount = computed(() => this.count() * 2); // Automatically updates when `count` changes

  increment() {
    this.count.set(this.count() + 1);
  }
}

doubleCount() updates only when count changes, making it more efficient than manual recalculations.

Effect Signals for Side Effects

Effect signals allow running side effects when a signal changes.

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

@Component({
  selector: 'app-timer',
  template: `<h2>Time: {{ time() }}</h2>`,
})
export class TimerComponent {
  time = signal(new Date().toLocaleTimeString());

  constructor() {
    effect(() => {
      console.log('Time updated:', this.time());
    });

    setInterval(() => {
      this.time.set(new Date().toLocaleTimeString()); // Updates every second
    }, 1000);
  }
}

The effect() function automatically tracks the signal and executes whenever time changes.

Signals provide a powerful, fine-grained, and efficient way to handle change detection in Angular, reducing reliance on Zone.js and manual optimizations. As Angular moves towards a zoneless future, adopting signals can lead to significant performance improvements.