MCPcopy
hub / github.com/angular/angular / toSignal

Function toSignal

packages/core/rxjs-interop/src/to_signal.ts:128–225  ·  view source on GitHub ↗
(
  source: Observable<T> | Subscribable<T>,
  options?: ToSignalOptions<T | U> & {initialValue?: U},
)

Source from the content-addressed store, hash-verified

126 * @see [RxJS interop with Angular signals](ecosystem/rxjs-interop)
127 */
128export function toSignal<T, U = undefined>(
129 source: Observable<T> | Subscribable<T>,
130 options?: ToSignalOptions<T | U> & {initialValue?: U},
131): Signal<T | U> {
132 typeof ngDevMode !== 'undefined' &&
133 ngDevMode &&
134 assertNotInReactiveContext(
135 toSignal,
136 'Invoking `toSignal` causes new subscriptions every time. ' +
137 'Consider moving `toSignal` outside of the reactive context and read the signal value where needed.',
138 );
139
140 const requiresCleanup = !options?.manualCleanup;
141
142 if (ngDevMode && requiresCleanup && !options?.injector) {
143 assertInInjectionContext(toSignal);
144 }
145
146 const cleanupRef = requiresCleanup
147 ? (options?.injector?.get(DestroyRef) ?? inject(DestroyRef))
148 : null;
149
150 const equal = makeToSignalEqual(options?.equal);
151
152 // Note: T is the Observable value type, and U is the initial value type. They don't have to be
153 // the same - the returned signal gives values of type `T`.
154 let state: WritableSignal<State<T | U>>;
155 if (options?.requireSync) {
156 // Initially the signal is in a `NoValue` state.
157 state = signal(
158 {kind: StateKind.NoValue},
159 {equal, ...(ngDevMode ? createDebugNameObject(options?.debugName, 'state') : undefined)},
160 );
161 } else {
162 // If an initial value was passed, use it. Otherwise, use `undefined` as the initial value.
163 state = signal<State<T | U>>(
164 {kind: StateKind.Value, value: options?.initialValue as U},
165 {equal, ...(ngDevMode ? createDebugNameObject(options?.debugName, 'state') : undefined)},
166 );
167 }
168
169 let destroyUnregisterFn: (() => void) | undefined;
170
171 // Note: This code cannot run inside a reactive context (see assertion above). If we'd support
172 // this, we would subscribe to the observable outside of the current reactive context, avoiding
173 // that side-effect signal reads/writes are attribute to the current consumer. The current
174 // consumer only needs to be notified when the `state` signal changes through the observable
175 // subscription. Additional context (related to async pipe):
176 // https://github.com/angular/angular/pull/50522.
177 const sub = source.subscribe({
178 next: (value) => state.set({kind: StateKind.Value, value}),
179 error: (error) => {
180 state.set({kind: StateKind.Error, error});
181 destroyUnregisterFn?.();
182 },
183 complete: () => {
184 destroyUnregisterFn?.();
185 },

Callers 11

to_signal_spec.tsFile · 0.90
TestCmpClass · 0.90
isStableFunction · 0.90
getControlStatusSignalFunction · 0.90
getControlEventsSignalFunction · 0.90
getControlValueSignalFunction · 0.90
EmbeddedEditorClass · 0.90
EditorUiStateClass · 0.90
PreviewClass · 0.90
constructorMethod · 0.90

Calls 13

assertInInjectionContextFunction · 0.90
injectFunction · 0.90
signalFunction · 0.90
computedFunction · 0.90
makeToSignalEqualFunction · 0.85
createDebugNameObjectFunction · 0.70
getMethod · 0.65
subscribeMethod · 0.65
setMethod · 0.65
onDestroyMethod · 0.65
bindMethod · 0.65

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…