liveBook · Manning

toSignal

As explained in Modern Angular

toSignal is an Angular RxJS interop function that creates a signal reflecting the latest value emitted by an observable.

toSignal converts an RxJS observable into an Angular signal whose value tracks the observable’s latest emission. It bridges asynchronous observable data into synchronous signal reads, so the result can feed computed signals and templates like any other signal. Because an observable may not emit immediately, provide initialValue when consumers need a usable value from the start. section 6.6.1

Listing 6.10 Converting an observable to a signal
export class TimeOffManagementComponent {
  private readonly timeOffRequestService = inject(
  TimeOffRequestService,
);  #1
  requests = toSignal(
    this.timeOffRequestService.getRequests(),
   {initialValue: []}
  );  #2
  selectedType = signal<
    'Vacation' | 'Sick Leave' | 'Maternity Leave' |
   'Paternity Leave' | 'Other' | ''
  >(localStorage.getItem('selectedType') as any ?? '');
  resolvedRequests = computed(() =>
    this.filteredRequests().filter((r) => r.status !== 'Pending')
  );
  filteredRequests = computed(() => {  #3
    const type = this.selectedType();
    return this.requests().filter(r => (type ? r.type === type : true));
  });
 #4
}

A time-off component converts an API-backed request stream to requests, initializes it as an empty array, and filters the resulting signal with computed state. section 6.6.1

The returned value is a Signal, not a WritableSignal. Read it with requests(), derive from it, or render it, but do not call update on it. Changes belong at the observable’s source—for example, by sending an HTTP request and then retrieving refreshed data—rather than mutating the UI-side signal. section 6.6.1

Account for observable timing and terminal behavior:

  • Use initialValue for an asynchronous source when readers cannot accept an initially missing value. section 6.6.1
  • Use requireSync only when the source is known to emit synchronously; it gives a non-undefined signal type and fails if no synchronous emission occurs. section 7.1.4
  • Expect a source error to be thrown when the signal is next read; after completion, expect the last emitted value to remain available. section 6.6.1

Real-time notification caveat

Wrapping a service-level notification observable in toSignal subscribes to that observable, which can open a web socket. If the signal outlives every consuming component, that subscription and socket can remain active. For shared, long-lived connections, use an explicit connection approach when lifecycle control is required rather than assuming toSignal alone determines when it is safe to disconnect. section 7.3.3

During migration, distinguish state from events. Synchronous state-holding observables can be tested by wrapping the existing source with toSignal, then progressively replacing source subjects with signals, derived observables with computed signals, and tap side effects with effects. Event-like observables need more caution. section 7.4.1

How Modern Angular covers it

How deep it goes
Explains conversion, initialization, readonly semantics, errors, completion, synchronous requirements, and subscription-lifetime caveats.
Examples
Uses time-off API requests, synchronous of emissions, and web-socket notifications.
What it assumes
Assumes working knowledge of Angular signals and RxJS observables, then clarifies their synchronous-versus-asynchronous mismatch.
What you can do after
Apply it via toSignal to expose observable values as readonly signals while choosing initialValue or requireSync appropriately.
How it connects
Connects observables with signals, WritableSignal, computed, effect, BehaviorSubject, HttpClient, and web sockets.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern Angular. The section links point to the parts of the book it draws on.