Volver al blog
Angular RxJS Signals Async

RxJS Interop: Uniendo lo mejor de dos mundos en Angular

13 min
German

Aunque Angular Signals es fantástico para el estado síncrono, RxJS sigue siendo el rey para manejar eventos asíncronos complejos (como debounce, switchMap o websockets). La clave del éxito en Angular moderno es saber cómo hacerlos trabajar juntos.

Angular proporciona el paquete @angular/core/rxjs-interop para facilitar esta comunicación.

De Observable a Signal: toSignal

El uso más común es consumir un flujo de datos (como una petición HTTP) en la vista sin usar el AsyncPipe. toSignal convierte un Observable en un Signal de lectura.

typescript
import { toSignal } from '@angular/core/rxjs-interop';
import { inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({...})
export class UserListComponent {
  private http = inject(HttpClient);
  
  // El observable se suscribe automáticamente
  users$ = this.http.get<User[]>('/api/users');
  
  // Convertimos a Signal. Podemos definir un valor inicial.
  users = toSignal(this.users$, { initialValue: [] });
}

En la plantilla, simplemente llamamos a users() y Angular se encarga de la reactividad fina.

De Signal a Observable: toObservable

A veces necesitamos reaccionar a cambios en un Signal usando operadores de RxJS (por ejemplo, para hacer un debounce en un input de búsqueda).

typescript
import { signal, effect } from '@angular/core';
import { toObservable } from '@angular/core/rxjs-interop';
import { debounceTime, switchMap } from 'rxjs/operators';

export class SearchComponent {
  query = signal('');
  
  // Convertimos el signal a observable para usar pipes de RxJS
  results$ = toObservable(this.query).pipe(
    debounceTime(300),
    switchMap(term => this.searchService.search(term))
  );

  updateQuery(e: Event) {
    this.query.set((e.target as HTMLInputElement).value);
  }
}

El poder de effect() vs subscribe()

Mientras que en RxJS nos suscribimos manualmente, con Signals usamos effect(). Un efecto se ejecuta siempre que uno de los signals que lee cambia.

Esta interoperabilidad nos permite eliminar gran parte de la complejidad de gestión de suscripciones manuales (ngOnDestroy o takeUntil).