DEV Community

Cover image for How to Rewrite Angular Apps to be Nearly Observable and Subscribe-Free
Doguhan Uluca
Doguhan Uluca

Posted on

How to Rewrite Angular Apps to be Nearly Observable and Subscribe-Free

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(); 
}
Enter fullscreen mode Exit fullscreen mode

We can bridge remaining Observables with helpers like:

import {toSignal} from '@angular/core'

const searchTerms$ = toSignal(termsObservable)
Enter fullscreen mode Exit fullscreen mode

We also need to set the change detection strategy to OnPush so components only update when their inputs change:

@Component({
  //...
  changeDetection: ChangeDetectionStrategy.OnPush
})
Enter fullscreen mode Exit fullscreen mode

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 {}
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
junlow profile image
jun

Nice article, Doguhan!

Collapse
 
teej107 profile image
teej107

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.

Collapse
 
jangelodev profile image
João Angelo

Hi, Doguhan Uluca,
Thanks for sharing