The State of Angular: Signals and Observables

Alberto Basalo
3 min readJun 24, 2024

--

State management, change detection with signals and observables.

Change detection has evolved more in the first six months of this year than in the previous six years. All thanks to the signal type and its interaction with the observable.

In this article, I summarize the recommended use of both techniques to improve the work experience for both users and programmers.

📻 Signals

  • Templates should consume signals to update with the least processing cost possible.
  • Signals can be mutable: the user or some process can change them, and in that case, the views are automatically re-evaluated.
  • However, most signals are inherently immutable; that is, they have an initial value that will not change. These signals come from inputs (from other components or the router) and from toSignal() based on an observable.
  • Other signals can be derived from these using the computed() function, without changing the initial value and re-evaluating only what has changed.

It is highly recommended to use computed functions to derive states that update the views intelligently.

🕵️ Observables

  • The types, functions, and operators of RxJs remain the most versatile tool for handling asynchronous processes, such as the common access to an API.
  • Especially if complex operators that can be channeled in pipes() are needed.

They require a subscription and, therefore, the responsibility to manage their un-subscription.

🌉 Interoperation

  • Since we use observables to access an API and signals to present data in the view, we will need something to interact and convert them.
  • Angular offers native functions to convert from one to the other.
  • The toObservable(sourceSignal) function emits a new observable value when the source signal’s state changes.
  • The toSignal(sourceObservable$) function subscribes, assigns values to the signal on each emission, and unsubscribes when the observable completes.

Perfect. Observables to fetch asynchronous data, and signals to represent them. Problem solved. Not so fast…

😈 The Problem

  • The toSignal(sourceObservable$) function is used when declaring a variable or property, returning an immutable signal type instance.
  • That is, it executes only once to instantiate the signal that it will mutate, but that you can only read.
  • It is ideal for displaying data whose sourceObservable$ argument is known from the initial declaration. Example:
dataSignal = toSignal(this.http.get(this.url), { initialValue: [] });typ
  • But, if there are arguments that change over time, you cannot easily determine the sourceObservable$, as the URL or payload data will be dynamic.
  • This frequently occurs when going to the API with variable filters (from the user, router parameters, other processes…).
dataSignal = toSignal(
this.http.get(this.url + this.someVariable),
{ initialValue: [] }
);

🔧 The Solution

  • Pipes with higher-order RxJS operators (first order for jedis).
  • The idea is that the sourceObservable$ can change, and for that, its argument must be able to change observably… and for that, operators like switchMap, concatMap, forkJoin… are used.
  • The final syntax can be a bit cumbersome, but it follows a consistent pattern and can easily be abstracted into functions that hide that complexity.
someVariable$ = new Subject(); // or toObservable(), any other RxJs source…
dataSignal = toSignal(
this.someVariable$.pipe(
switchMap(
(someVariable) => this.http.get(this.url + someVariable)
)
),
{initialValue: [],}
);

Each time an input parameter changes, the observable stream is re-hydrated, changing as needed to another observable that will finally feed the signal.

💻 The Code

  • As a real use example you can follow this one:
  • A page where the productId can change at any moment.
  • What we are interested in is the Product object that comes from the API.
  • We want to return that object as a signal for the view, but without worrying about the RxJs subscription.

[Code example]

https://github.com/AlbertoBasalo/ActionBuy/blob/master/src/app/routes/buy/buy.store.ts

🔮 The Future

  • These technologies are very recent, and new ways of resolving the synchronous/asynchronous impedance of signals and observables may appear.

Follow me to stay up to date. I am your observable to signal 😜

Alberto Basalo

--

--

Alberto Basalo

Advisor and instructor for developers. Keeping on top of modern technologies while applying proven patterns learned over the last 25 years. Angular, Node, Tests