Back to blog

Reactivity in Angular: Signals vs Observables

Angular applications often deal with asynchronous data, making reactive programming a powerful tool for managing data flow and building responsive UIs. Within this paradigm, two key concepts emerge: Signals and Observables (the latter powered by RxJS). While both handle data streams, they have distinct characteristics that influence when to choose one over the other. This blog delves into their functionalities, strengths, and weaknesses, guiding you towards the ideal choice for your Angular project.

Understanding Reactive Programming

Reactive programming is a programming paradigm that focuses on data streams and the reactions they trigger. Imagine a river; data flows downstream, and components along the riverbank react to changes in the water level (data). Observables are the fundamental building blocks of reactive programming in Angular. They represent streams of values that can be emitted over time, allowing components to subscribe and react to these emissions.

Enter Signals: A Simpler Approach

Introduced in Angular version 16, Signals offer a lighter alternative to RxJS Observables. They act as wrappers around a value, notifying interested components whenever the value changes. Signals are designed to be simpler to use, especially for common UI-related asynchronous operations within Angular.

Key Differences

While both Signals and Observables handle data streams, they differ in crucial aspects:

Choosing the Right Tool: Signals vs Observables

The decision between Signals and Observables hinges on your project's requirements:

Examples

1. Updating a counter based on user clicks using a Signal:

Simple Value Change (Signal)
1
2import { Component } from '@angular/core';
3import { Signal } from '@angular/core';
4
5@Component({
6 selector: 'app-counter',
7 template: `
8 <button (click)="increment()">Increment</button>
9 <span>{{ count }}</span>
10 `
11})
12export class CounterComponent {
13 count = 0;
14 countChanged = new Signal<number>(this.count);
15
16 increment() {
17 this.count++;
18 this.countChanged.emit(this.count);
19 }
20}
21
Explanation:

2. Utilizing RxJS Observables to implement a debounced search with error handling:

Debounced Search with Error Handling (Observable)
1
2import { Component } from '@angular/core';
3import { fromEvent, debounceTime, distinctUntilChanged, tap, catchError } from 'rxjs/operators';
4import { of } from 'rxjs';
5
6@Component({
7 selector: 'app-search',
8 template: `
9 <input type="text" [(ngModel)]="searchTerm">
10 <div *ngIf="searchResults.length > 0">Results: {{ searchResults }}</div>
11 <div *ngIf="error">Error: {{ error }}</div>
12 `
13})
14export class SearchComponent {
15 searchTerm = '';
16 searchResults: any[] = [];
17 error: string | null = null;
18
19 ngOnInit() {
20 fromEvent(this.searchInputRef.nativeElement, 'keyup')
21 .pipe(
22 map(event => (event.target as HTMLInputElement).value),
23 debounceTime(400),
24 distinctUntilChanged(),
25 tap(() => this.error = null), // Clear previous error before search
26 catchError(error => {
27 this.error = error.message;
28 return of([]); // Emit an empty array to prevent unexpected behavior
29 })
30 )
31 // preferably use async pipes, but not doing so for brevity
32 .subscribe(searchTerm => this.fetchSearchResults(searchTerm));
33 }
34}
35
Explanation:

These examples showcase the core differences:

Conclusion

In conclusion, signals and observables are powerful tools in the toolkit of Angular developers, offering distinct advantages and use cases. Signals are best suited for scenarios where direct control over data emission is required, such as event handling and state management. Observables, on the other hand, excel at handling asynchronous data streams and performing complex transformations. By understanding the differences between signals and observables, and knowing when to use each approach, developers can build more robust and maintainable Angular applications. Whether it's handling user interactions, making HTTP requests, or managing application state, signals and observables provide the building blocks for creating responsive and interactive user experiences in Angular.