Understanding Signals in Angular: A Comprehensive Guide

Angular is an ever-evolving framework, and with its continuous improvements, it introduces new concepts to make web development more efficient and manageable. One such powerful addition to Angular’s reactivity model is Signals. Signals in Angular aim to enhance the reactivity system, providing a more efficient, granular, and predictable way to manage state and changes in applications.

Vítor Azevedo
5 min readSep 6, 2024

In this article, we will dive deep into understanding Signals in Angular, their use cases, how they differ from other state management techniques, and provide a complete working example to illustrate their practical usage.

What are Signals in Angular?

Signals are a new reactive primitive introduced in Angular to manage state and reactivity more predictably and efficiently. Inspired by the concept of reactive programming, Signals provide a more granular control over change detection and data flow, enabling developers to optimize performance and improve state management in Angular applications.

Unlike traditional Angular reactive mechanisms (such as Observables and ChangeDetectionStrategy), Signals offer a push-based reactivity model where updates are only triggered when explicitly signaled, leading to more efficient change detection and reduced unnecessary computations.

Key Features of Signals:

  1. Predictability: Signals provide a clear and straightforward mechanism to understand when and how data changes occur.
  2. Granular Control: Signals work on a fine-grained level, meaning you can control updates and reactivity more precisely.
  3. Improved Performance: Since Signals reduce unnecessary change detections, they can lead to significant performance gains in large and complex applications.
  4. Interoperability: Signals work well with existing Angular features like Observable, Input, Output, and ChangeDetectionStrategy.

Why Use Signals?

The primary goal of introducing Signals in Angular is to provide a more efficient way to manage application state changes. This can be especially beneficial in scenarios where:

  • There are frequent state changes, and minimizing change detection checks can significantly improve performance.
  • You need more granular control over reactivity.
  • You want a simpler, more predictable approach to state management.

Signals vs. Observables

Both Signals and Observables are reactive primitives, but they differ in how they handle reactivity:

Reactivity Model
Signals
: Push-based (explicit updates)
Observables: Pull-based (subscription and emissions)

Change Detection
Signals
: Granular and explicit control
Observables: Managed by the Angular zone

Use Case
Signals
: Optimized state management
Observables: Asynchronous data streams

API Complexity
Signals
: Simple and direct
Observables: Requires understanding of RxJS operators and concepts

Signals are not meant to replace Observables but to complement them, providing a simpler alternative for specific use cases where fine-grained reactivity is needed.

Core Concepts of Signals in Angular

To understand Signals better, let’s dive into some of the core concepts and APIs provided by Angular for working with Signals:

1. Signal

The signal function creates a signal object that holds a reactive value. You can think of it as a reactive variable that notifies its subscribers whenever its value changes. Signals are read-only by default, but you can change their value using a specific API.

const mySignal = signal(initialValue);

2. Computed

computed is a function that creates a computed signal, which derives its value based on other signals. When any of the dependent signals change, the computed signal automatically recalculates its value.

const computedSignal = computed(() => signal1() + signal2());

3. Effect

effect is a function that allows you to run side effects whenever a signal value changes. Effects are useful for performing operations like API calls, DOM manipulations, or other side effects.

effect(() => {
console.log('Signal value changed:', mySignal());
});

4. Watch

watch is similar to effect, but it allows you to watch specific signals and run side effects when their values change. It provides more control compared to effect.

watch(mySignal, (newValue, oldValue) => {
console.log('Signal value changed from', oldValue, 'to', newValue);
});

Step-by-Step Example: Using Signals in an Angular Application

Let’s build a complete example to demonstrate how to use Signals in an Angular application. We’ll create a simple Counter application that utilizes Signals for managing state changes.

Step 1: Setting Up the Angular Application

First, let’s create a new Angular application:

ng new angular-signals-demo --routing --style=scss
cd angular-signals-demo
ng add @angular/material

Step 2: Creating the Signal-Based Counter Component

Create a new component for the counter:

ng generate component counter

Edit the counter.component.ts file as follows:

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

@Component({
selector: 'app-counter',
templateUrl: './counter.component.html',
styleUrls: ['./counter.component.scss']
})
export class CounterComponent {
// Step 1: Create Signals
count = signal<number>(0);

// Step 2: Create Computed Signal
doubledCount = computed(() => this.count() * 2);

// Step 3: Create an Effect to Watch Changes
constructor() {
effect(() => {
console.log('Count changed:', this.count());
});
}

// Step 4: Methods to Update the Signal
increment() {
this.count.set(this.count() + 1);
}

decrement() {
this.count.set(this.count() - 1);
}
}

Step 3: Updating the Counter Component Template

Edit the counter.component.html file to include buttons for incrementing and decrementing the counter:

<div class="counter-container">
<h1>Counter with Signals</h1>
<p>Current Count: {{ count() }}</p>
<p>Doubled Count: {{ doubledCount() }}</p>
<button mat-raised-button color="primary" (click)="increment()">Increment</button>
<button mat-raised-button color="warn" (click)="decrement()">Decrement</button>
</div>

Step 4: Adding Styles for the Counter Component

Edit the counter.component.scss file to add some styles:

.counter-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;

button {
margin: 5px;
}
}

Step 5: Including the Counter Component in the Main Application

Edit the app.component.html file to include the Counter component:

<app-counter></app-counter>

Step 6: Running the Application

Run the application using the following command:

ng serve

Open your browser and navigate to http://localhost:4200 to see the Counter application in action.

Conclusion

Signals in Angular provide a more efficient and predictable way to manage state and reactivity in Angular applications. They offer fine-grained control over change detection and can significantly improve performance in applications with frequent state changes. By combining signal, computed, effect, and watch APIs, developers can easily manage reactive state without the complexity of Observables.

In this article, we’ve covered the core concepts of Signals, how they compare to Observables, and provided a complete working example to illustrate their practical usage. As Angular continues to evolve, adopting Signals can be a powerful tool for developers looking to build more efficient and maintainable applications.

--

--

Vítor Azevedo
Vítor Azevedo

Written by Vítor Azevedo

Frontend Developer with 25+ years' expertise in HTML, CSS, JavaScript, Angular and Vue. Builds dynamic, user-centric web apps. Award-winning projects.