Angular Signals and Observables: A Clean Pattern for Managing State

Introduction

In modern Angular applications, handling reactive data streams efficiently is crucial. This pattern ensures:

  • Separation of concerns – Data fetching and UI reactivity are managed separately.
  • Optimised reactivity – Using signals to prevent unnecessary UI updates.
  • Scalability – A structured approach that can be extended with facades if needed.

This post presents a pattern using two services:

  1. A data service that fetches and exposes data via observables.
  2. A signal-based service that converts the observable into a signal for UI components.

Note: When I wrote this post, I was working on a team that did not want observables in components. To handle this, you could introduce a facade service that wraps everything in signals—including the startFeed observable—and expose it via a view model. The component would then trigger the data flow by calling a signal function in the template.


1. Data Service: Handling the Data Fetching

This service is responsible for fetching data and exposing an observable.

Key Points:

  • startFeed() fetches data and updates the BehaviorSubject.
  • listen() exposes the current state as an observable for external consumers.

2. Signal Service: Converting the Observable into a Signal

This service wraps the observable into a signal and provides derived signals for specific properties.

Key Points:

  • toSignal() creates a reactive signal from the observable.
  • Computed signals ensure only necessary properties are exposed.
  • startFeed() is exposed so it can be subscribed to in the template using async.

3. Using the Signal Service in a Component

The component subscribes to “ using the async pipe, ensuring it follows reactive best practices.

Key Points:

  • start() assigns feed$ to startFeed() and binds it in the template using async.
  • dataTitle is a signal, meaning it updates automatically when new data arrives.

Alternative Approach: Using a Facade Service

If you don’t want observables in the component, you could introduce a facade service that exposes everything as signals—including the startFeed() observable wrapped inside a signal.

Then, in the component:

This approach keeps all observable logic out of the component while maintaining a clean reactive API.


Conclusion

This pattern provides:

Separation of concerns – Data fetching, state management, and UI logic are cleanly divided.

Optimised rendering – UI only updates when necessary.

Flexibility – Works with or without observables in the component.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *