How to Rewrite Angular Apps to be Nearly Observable and Subscribe-Free
Tags: webdev, angular, typescript, rxjs
Published: 2024-01-21 18:28:44

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](https://angular.io/guide/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](https://ngrx.io/guide/signals/signal-store) state management library, we can rewrite Angular apps to be nearly Observable and subscribe-free. I recently did this with the sample [LocalCast Weather](https://localcastweather.app) app from my book [Angular for Enterprise Applications](https://angularforenterprise.com) and I want to share an overview.

## Replacing Observables with Promises and Signals

The first key is to replace Observable HTTP responses and [BehaviorSubject](https://rxjs.dev/api/index/class/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](https://angularforenterprise.com) on [GitHub](https://github.com/duluca/local-weather-app/tree/main/projects/signal-store). 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](https://angularforenterprise.com) to master advanced Angular development!