Managing subscriptions is one of the trickiest parts of working with RxJS and Observables in Angular. Even with helpers like the async pipe, takeUntilDestroyed, and auto-unsubscribe, it's easy to run into bugs and memory leaks. The new Signals feature in Angular aims to solve this problem by introducing a simpler, subscription-less reactive programming model.
In combination with the powerful new NgRx SignalStore state management library, we can rewrite Angular apps to be nearly Observable and subscribe-free. I recently did this with the sample LocalCast Weather app from my book Angular for Enterprise Applications and I want to share an overview.
Replacing Observables with Promises and Signals
The first key is to replace Observable HTTP responses and BehaviorSubject data anchors with Promise-based APIs instead. NgRx SignalStore uses signals under the hood, which manage their own subscriptions:
async getCurrentWeather(searchText, country) {
return promiseBasedWeatherApiCall();
}
We can bridge remaining Observables with helpers like:
import {toSignal} from '@angular/core'
const searchTerms$ = toSignal(termsObservable)
We also need to set the change detection strategy to OnPush
so components only update when their inputs change:
@Component({
//...
changeDetection: ChangeDetectionStrategy.OnPush
})
Migrating to SignalStore State Management
NgRx SignalStore offers a simple API for state management using signals:
const store = signalStore(
withState({
currentWeather: initialWeather,
}),
withMethods((
store,
weatherService = inject(WeatherService)
) => ({
async updateWeather(search) => {
const weather = await apiCall(search)
patchState(store, {currentWeather: weather})
}
})
)
@Component({
template: `
{{store.currentWeather | json}}
`
})
export class WeatherComponent {}
So rather than loading data in services and pushing changes through subjects, component store methods retrieve the data and update signals. This encapsulates data fetching cleanly without needing intermediary streams. Components simply bind to the relevant state signals.
The result is much leaner code without requiring explicit subscription handling. We get great ergonomics leveraging signals, while still centralizing state management logic with SignalStore.
Ready for Angular's Signal-based Future
While Observables and RxJS offer tremendous declarative reactive power, they also entail complexity. Signals aim to simplify reactive Angular programming. Combined with state management options like NgRx SignalStore, we can eliminate nearly all subscriptions from our apps.
Conclusion
I've shared a full SignalStore migration of the LocalCast Weather app from Angular for Enterprise Applications on GitHub. This book covers architectural patterns like this to build robust, real-world Angular applications.
The updated 3rd edition with Angular 17.1 will be released on January 31st, 2024. Check it out at AngularForEnterprise.com to master advanced Angular development!
Top comments (3)
Nice article, Doguhan!
I'd argue that replacing Observables with Promises/Signals is not necessary. Using Observables are already subscription-less when using the async pipe. Using Promises can make writing tests more difficult since you have to deal with the microtask queue.
Hi, Doguhan Uluca,
Thanks for sharing