Angular Signals: The Future of State Management in Angular

Astrit Shuli
Stackademic
Published in
6 min readOct 25, 2023

--

Angular Signals: The Future of State Management in Angular. Photo Credits: Astrit Shuli
Angular Signals: The Future of State Management in Angular. Photo Credits: Astrit Shuli.

Angular Signals is a new feature introduced in Angular 16 that is set to revolutionize the way change detection is handled in Angular applications. Signals provide a more granular and efficient way to track changes in state, which can lead to significant performance improvements, especially in large and complex applications.

What are Signals?

A signal is a wrapper around a value that can notify interested consumers when that value changes. Signals can contain any value, from simple primitives to complex data structures. Signals may either be writable or read-only.

Signals are similar to Observables, but there are a few key differences. First, signals are designed to be used for change detection, while Observables are more general-purpose. Second, signals are immutable, meaning that they cannot be changed directly. Instead, a new signal must be created with the updated value. This makes signals easier to reason about and prevents unexpected side effects.

Here is an example of how to use signals in an Angular application:

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

export class AppComponent {
count: WritableSignal<number> = signal(0);
doubleCount: Signal<number> = computed(() => this.count() * 2);

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

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

In this example, we define two signals: count and doubleCount. The count signal is a writable signal, meaning that its value can be changed. The doubleCount signal is a computed signal, meaning that its value is derived from the count signal.

The computed function takes a derivation function as its argument. The derivation function is called whenever the signals that it depends on change. In this case, the doubleCount signal depends on the count signal. This means that the doubleCount signal will be updated whenever the count signal changes.

To use the signals, we can simply subscribe to them. For example, we could subscribe to the doubleCount signal and update the DOM whenever its value changes:

<p>The double count is {{ doubleCount | async }}</p>

Why use Signals?

There are several benefits to using signals in Angular applications:

  • Performance: Signals can lead to significant performance improvements by reducing the number of change detections that are required. This is because signals are more granular than the current change detection mechanism, which is based on dirty checking.
  • Simplicity: Signals are easier to use and reason about than the current change detection mechanism. This is because signals are immutable and have a clear API.
  • Flexibility: Signals can be used to implement a variety of different reactivity patterns, such as derived signals, memoization, and lazy loading.

Unique use cases for Signals

Here are a few unique use cases for signals in Angular applications:

  • Real-time data synchronization: Signals can be used to synchronize real-time data between different components in an Angular application. This can be useful for building applications such as chat apps and dashboards.
  • Efficient animation: Signals can be used to efficiently animate elements in an Angular application. This is because signals can be used to track changes in state and only update the DOM when necessary.
  • Lazy loading: Signals can be used to implement lazy loading of components and modules in an Angular application. This can improve the performance of applications by loading only the components and modules that are actually needed.

Advanced use cases for Signals

In addition to the use cases listed above, signals can also be used to implement more advanced reactivity patterns, such as:

  • State machines: Signals can be used to implement state machines in Angular applications. This can be useful for building complex applications with multiple states.
  • UI interactions: Signals can be used to implement complex UI interactions, such as drag-and-drop and resizing.
  • Data validation: Signals can be used to implement data validation in Angular applications. This can be useful for ensuring that the data entered by users is valid.

Examples of using Signals

Here are a few examples of how to use signals in Angular applications:

Example 1: Real-time data synchronization

The following example shows how to use signals to synchronize real-time data between two components:

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

export class ChatComponent {
messages: Signal<string[]> = signal([]);

sendMessage(message: string) {
this.messages.push(message);
}
}

export class MessageListComponent {
messages: Signal<string[]> = signal([]);

ngOnInit() {
this.messages.subscribe(messages => {
this.messages = messages;
});
}
}

In this example, the ChatComponent has a signal called messages that contains an array of messages. The MessageListComponent also has a signal called messages that contains an array of messages.

When the user sends a message in the ChatComponent, the messages signal is updated. The MessageListComponent is subscribed to the messages signal, so it is updated whenever the messages signal changes. This ensures that the MessageListComponent always displays the latest messages.

Example 2: Efficient animation

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

export class AppComponent {
// Define a signal for the element to be animated.
element: Signal<HTMLElement> = signal(null);

// Subscribe to the signal and update the DOM accordingly.
ngOnInit() {
this.element.subscribe(element => {
// Animate the element.
});
}

// Update the state using a writable signal.
setElement(element: HTMLElement) {
this.element.set(element);
}
}

In this example, we use a signal to track the element to be animated. We also subscribe to the signal and update the DOM accordingly whenever the signal emits. Finally, we use a writable signal to update the element to be animated.

To use this example, we would first need to create a template that contains the element that we want to animate. For example:

<div id="my-element"></div>

Then, we would need to inject the AppComponent into your component and assign the element to the element signal. For example:

import { Component } from '@angular/core';
import { AppComponent } from './app.component';

@Component({
selector: 'my-component',
templateUrl: './my-component.component.html'
})
export class MyComponent {
constructor(private appComponent: AppComponent) {}

ngOnInit() {
this.appComponent.element.set(document.getElementById('my-element'));
}
}

Finally, we would need to write the code to animate the element. For example:

import { animate, style } from '@angular/animations';

@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
animations: [
animate('1s', style({
transform: 'translateY(100px)'
}))
]
})
export class MyComponent {
constructor(private appComponent: AppComponent) {}

ngOnInit() {
this.appComponent.element.set(document.getElementById('my-element'));
}

animate() {
// Animate the element.
this.appComponent.element.value.classList.add('animated');
}
}

When we call the animate() method, the element will be animated to move 100 pixels down the page.

Example 3: Derived signals

The following example shows how to use signals to implement a derived signal:

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

export class AppComponent {
count: WritableSignal<number> = signal(0);
isEven: Signal<boolean> = computed(() => this.count() % 2 === 0);

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

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

In this example, the isEven signal is a derived signal that depends on the count signal. Whenever the count signal changes, the isEven signal is updated accordingly.

The isEven signal can then be used to conditionally render elements in the DOM. For example, we could render a different color depending on whether the isEven signal is true or false:

<p class="even" *ngIf="isEven | async">The count is even.</p>
<p class="odd" *ngIf="!isEven | async">The count is odd.</p>

Example 4: Memoization

The following example shows how to use signals to implement memoization:

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

export class AppComponent {
expensiveComputation: Signal<number> = memoized(() => {
// Perform an expensive computation here.
return 123;
});

render() {
// Display the result of the expensive computation.
return this.expensiveComputation();
}
}

In this example, the expensiveComputation signal is a memoized signal. This means that the computation is only performed once, and the result is cached. Subsequent calls to the expensiveComputation signal simply return the cached result.

This can be useful for improving the performance of applications that perform expensive computations.

Example 5: Lazy loading

The following example shows how to use signals to implement lazy loading:

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

export class AppComponent {
modules: Signal<Array<() => Promise<any>>> = signal([]);

loadModule(moduleName: string) {
const moduleLoader = lazy(() => import(`./modules/${moduleName}.module`));
this.modules.push(moduleLoader);
}
}

In this example, the modules signal contains an array of functions that load modules. When the user clicks on a button to load a module, the loadModule() method is called. This method adds a module loader function to the modules signal.

The modules signal is then subscribed to. Whenever the modules signal changes, the module loader functions are executed. This loads the modules on demand.

Conclusion

Angular Signals is a powerful new feature that can be used to improve the performance, simplicity, and flexibility of Angular applications. While signals are still in developer preview, they are definitely worth checking out if you are looking for ways to improve your Angular applications.

Stackademic

Thank you for reading until the end. Before you go:

  • Please consider clapping and following the writer! 👏
  • Follow us on Twitter(X), LinkedIn, and YouTube.
  • Visit Stackademic.com to find out more about how we are democratizing free programming education around the world.

--

--