Angular Signals or RxJS - They are not a replacement
Angular Signals or RxJS - They are not a replacement

A lot of people say that we don't need RxJS anymore because we have Angular Signals. We can fully migrate to signals and remove RxJS completely. But it is not true and in this post I want to show you that RxJS still shines and we can mix it easily with signals.

Initial project

Here I prepared a small project with a single input.

Initial project

 <input type="text" (keyup)="search($event)" />

It has just an input with a keyup event.

 export class AppComponent {
  http = inject(HttpClient);

  search(event: Event) {
    const value = (event.target as HTMLInputElement).value;
  }
}

Inside our class I injected http and created a search function which gets a value of the input but doesn't do anything else.

Implementing API call

Now the question is how to implement fetching data from the API on initialize and after typing something inside the input with debounce so we are not hitting our API too often and ignoring all values of submitting which are already there.

This is exactly the case where RxJS shines in comparison to signals.

What we are getting from signals is simply a state to read or update the value and computed which returns some value based on another signal. The last thing that we get from signals is effect which allows us to do something when our signal is changed.

This is not what we need for our case here. We have the case where RxJS can help us a lot.

export class AppComponent {
  ...
  searchSig = signal<string>('');

  search(event: Event) {
    const value = (event.target as HTMLInputElement).value;
    this.searchSig.set(value);
  }
}

Our first step here is to create a signal which stores our search value. Now we can update this signal when we change the input with set function.

But it is not all. We want to make an API call every single time when we change our search. The main problem is that we can't write this code with signal effectively. Now we have a function in Angular which allows us to transform signal to the observable.

import { toObservable, toSignal } from '@angular/core/rxjs-interop';

export class AppComponent {
  searchSig = signal<string>('');
  articles$ = toObservable(this.searchSig).pipe(
    debounceTime(300),
    distinctUntilChanged(),
    switchMap((searchValue) =>
      this.http.get<ArticleInterface[]>(
        `http://localhost:3004/articles?title_like=${searchValue}`
      )
    )
  );
}

Here we used toObservable function to convert our signal to observable. It allows us to write RxJS functions on it. We used debounceTime to avoid calling API too often, distinctUntilChanged to avoid calling API with the same data and switchMap to make an HTTP request.

So this code just creates an observable from the signal.

It doesn't mean that we are back to our previous bad practices like async pipes or subscribes. We don't want to return to digest cycle and all it's problems.

export class AppComponent {
  ...
  articlesSig = toSignal(this.articles$);
}

This is why here we created one more property which is a signal. We used toSignal function to convert our RxJS observable to a signal. Not we can safely use it inside our markup is a normal signal.

<div *ngFor="let article of articlesSig()">
  {{ article.title }}
</div>

finished project

We can see that our API call is done and we rendered a list of article. When we type something it refetches the data with the rules that we wrote in RxJS.

As you can see the mix of RxJS and Angular signals is extremely versatile tool that can improve your application.

And actually if you want to learn Angular with NgRx from empty folder to a fully functional production application make sure to check my Angular and NgRx - Building Real Project From Scratch course.

📚 Source code of what we've done