home / skills / hoangnguyen0403 / agent-skills-standard / rxjs-interop

rxjs-interop skill

/skills/angular/rxjs-interop

This skill helps you bridge RxJS observables and signals using toSignal and toObservable for seamless template rendering and reactive logic.

npx playbooks add skill hoangnguyen0403/agent-skills-standard --skill rxjs-interop

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
1.0 KB
---
name: RxJS Interop
description: Bridging Observables and Signals using toSignal and toObservable.
metadata:
  labels: [angular, rxjs, signals, interop]
  triggers:
    files: ['**/*.ts']
    keywords: [toSignal, toObservable, takeUntilDestroyed, rxjs angular]
---

# RxJS Interop

## **Priority: P1 (HIGH)**

## Principles

- **Async to Sync**: Use `toSignal` to convert Observables (HTTP, Events) to Signals for template rendering.
- **Sync to Async**: Use `toObservable` when you need RxJS operators (debounce, switchMap) on a Signal.
- **Auto-Unsubscribe**: `toSignal` automatically unsubscribes.
- **Cleanup**: Use `takeUntilDestroyed` for manual subscriptions in injection contexts.

## Guidelines

- **HTTP Requests**:
  - GET: `http.get().pipe(...)` -> `toSignal()`
  - POST/PUT: Trigger explicit subscribe() or lastValueFrom().
- **Race Conditions**: Handle async loading states. `toSignal` requires an `initialValue` or handles `undefined`.

## References

- [Signals vs Observables](references/observables-vs-signals.md)

Overview

This skill documents practical patterns for bridging RxJS Observables and Angular Signals using toSignal and toObservable. It provides concise rules for when to convert streams to signals, when to treat signals as observables, and how to avoid common pitfalls like leaks and race conditions. The guidance targets HTTP, event streams, and injection-context subscriptions in TypeScript Angular apps.

How this skill works

toSignal converts an Observable into a Signal suitable for template rendering and automatically unsubscribes when the consuming component is destroyed. toObservable converts a Signal into an Observable to enable RxJS operators such as debounceTime, switchMap, and combineLatest. For manual subscriptions inside services or constructors, use takeUntilDestroyed to ensure proper cleanup.

When to use it

  • Use toSignal for HTTP GET and event streams that drive templates and component view state.
  • Use toObservable when you need RxJS operators (debounce, throttle, switchMap) on a reactive value.
  • Prefer toSignal for simple async-to-sync conversions to avoid manual subscription management.
  • Use explicit subscribe() or lastValueFrom() for POST/PUT requests where you need a one-off side effect.
  • Use takeUntilDestroyed in injection contexts (services, providers) for manual subscriptions that need cleanup.

Best practices

  • Always provide an initialValue to toSignal when a defined value is required to avoid undefined in templates.
  • Avoid calling toSignal on long-lived Observables without an initial value unless you handle loading state explicitly.
  • Use toObservable only when you need operator chains; otherwise prefer Signals for simpler reactivity.
  • Rely on toSignal’s auto-unsubscribe for component lifecycle; use takeUntilDestroyed for manual subscriptions in constructors or services.
  • Manage loading and error states explicitly to prevent race conditions when multiple async sources update the same signal.

Example use cases

  • Convert http.get() Observable to a Signal for direct use in templates: http.get(...).pipe(...).toSignal(initialValue).
  • Debounce user input from a Signal by converting it to an Observable, applying debounceTime, then mapping to a request.
  • Use toSignal for event streams (WebSocket, user events) that update UI state and should auto-unsubscribe with the component.
  • Trigger side-effect POST/PUT operations with explicit subscribe() or await lastValueFrom for one-shot semantics.
  • In a service constructor, subscribe to an Observable and use takeUntilDestroyed to avoid memory leaks.

FAQ

Do I always need to pass an initialValue to toSignal?

Not always, but passing an initialValue avoids undefined in templates and reduces race-condition risks. If you expect delayed emissions, provide a sensible default and manage loading state.

When should I use toObservable instead of directly using Signals?

Use toObservable when you need RxJS operators (debounceTime, switchMap, combineLatest) or when interacting with existing Observable-based APIs. For straightforward UI binding, Signals are simpler and preferred.