Skip to the solution
MediumFrontend9 min read

How do toSignal() and toObservable() bridge Signals and RxJS?

735 views

The problem

Question presented to candidate: "A teammate has a BehaviorSubject in a service and a signal-based component, and keeps writing subscribe() calls to glue them together. Explain how toSignal() and toObservable() connect the two worlds, and what each one actually emits."

What a strong answer should cover:

  • Both live in @angular/core/rxjs-interop and are stable (the 22.2.3 typings mark them @publicApi 20.0).
  • toSignal(obs$) subscribes immediately, stores each value in a signal, and unsubscribes when the injection context is destroyed. Before the first emission the value is undefined unless you pass initialValue or requireSync: true.
  • If the Observable errors, reading the signal throws that error; completion simply freezes the last value.
  • toObservable(sig) is implemented with an effect feeding a ReplaySubject(1): it emits asynchronously, only the settled value (several synchronous set() calls become one emission), and late subscribers get the latest value.
  • Both need an injection context (or an injector option); toSignal() must not be called inside computed()/effect() (NG0602).
  • Typical uses: toSignal(http$) or toSignal(route.paramMap) for templates; toObservable(query) to apply debounceTime/switchMap.

Clarifying questions expected:

  • "Does the service need to stay Observable-based for other consumers, or can it expose a signal?"
  • "Does the template need a value before the first emission?"

Code / implementation expected: Yes β€” an interval turned into a signal, and a signal turned into an Observable that shows which values it really emits.

  • interop
  • signals
  • rxjs

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 9 sections

Read the code

AngularBoth directions: interval to signal, signal to Observable (only settled values)
import { Component, signal } from "@angular/core";
import { toObservable, toSignal } from "@angular/core/rxjs-interop";
import { interval, map, scan } from "rxjs";

@Component({
  selector: "app-root",
  template: `
    <p>Seconds on page: {{ seconds() }}</p>
    <p>count = {{ count() }}</p>
    <button (click)="count.update(n => n + 1)">+1</button>
    <button (click)="plusThree()">+1 three times</button>
    <p>values the Observable saw: {{ seen().join(", ") }}</p>
  `,
})
export class AppComponent {
  // Observable -> Signal: toSignal subscribes now and unsubscribes when this component is destroyed.
  seconds = toSignal(interval(1000).pipe(map((i) => i + 1)), { initialValue: 0 });

  // Signal -> Observable: toObservable uses an effect, so it emits the settled value, not every set().
  count = signal(0);
  seen = toSignal(
    toObservable(this.count).pipe(
      map((n) => String(n)),
      scan((all, n) => [...all, n], [] as string[]),
    ),
    { initialValue: [] as string[] },
  );

  plusThree() {
    this.count.update((n) => n + 1);
    this.count.update((n) => n + 1);
    this.count.update((n) => n + 1);
  }
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 38 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextMedium

Explain the difference between Shadow DOM, Emulated, and None View Encapsulation with compile output.

Continue