mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
a1973a4216
PR Close #50614
104 lines
4.3 KiB
Markdown
104 lines
4.3 KiB
Markdown
# RxJS Interop
|
|
|
|
<div class="alert is-important">
|
|
|
|
The RxJS Interop package is available for [developer preview](/guide/releases#developer-preview). It's ready for you to try, but it might change before it is stable.
|
|
|
|
</div>
|
|
|
|
Angular's `@angular/core/rxjs-interop` package which provides useful utilities to integrate [Angular Signals](/guide/signals) with RxJS Observables.
|
|
|
|
## `toSignal`
|
|
|
|
The `toSignal` function creates a signal which tracks the value of an Observable. It behaves similarly to the `async` pipe in templates, but is more flexible and can be used anywhere in an application.
|
|
|
|
```ts
|
|
import { Component } from '@angular/core';
|
|
import { AsyncPipe } from '@angular/common';
|
|
import { interval } from 'rxjs';
|
|
|
|
@Component({
|
|
template: `{{ counter() }}`,
|
|
})
|
|
export class Ticker {
|
|
counterObservable = interval(1000);
|
|
|
|
// Get a `Signal` representing the `counterObservable`'s value.
|
|
counter = toSignal(this.counterObservable, {initialValue: 0});
|
|
}
|
|
```
|
|
|
|
Like the `async` pipe, `toSignal` subscribes to the Observable immediately, which may trigger side effects. The subscription created by
|
|
`toSignal` automatically unsubscribes from the given Observable upon destruction of the component in which `toSignal` is called.
|
|
|
|
### Initial values
|
|
|
|
Observables may not produce a value synchronously on subscription, but signals always require a current value. There are several ways to deal with this "initial" value of `toSignal` signals.
|
|
|
|
#### The `initialValue` option
|
|
|
|
As in the example above, the `initialValue` option specifies the value the signal should return before the Observable emits for the first time.
|
|
|
|
#### `undefined` initial values
|
|
|
|
If `initialValue` is omitted, the signal returned by `toSignal` returns `undefined` until the Observable emits. This is similar to the `async` pipe's behavior of returning `null`.
|
|
|
|
#### The `requireSync` option
|
|
|
|
Some Observables are known to emit synchronously, such as `BehaviorSubject`. In those cases, you can specify the `requireSync: true` option.
|
|
|
|
When `requiredSync` is `true`, `toSignal` enforces that the Observable emits synchronously on subscription. This guarantees that the signal always has a value, and no `undefined` type or initial value is required.
|
|
|
|
### `manualCleanup`
|
|
|
|
By default, `toSignal` automatically unsubscribes from the Observable upon destruction of the context in which it's created. For example, if `toSignal` is called during creation of a component, it cleans up its subscription when the component is destroyed.
|
|
|
|
The `manualCleanup` option disables this automatic cleanup. You can use this setting for Observables that complete themselves naturally.
|
|
|
|
### Error and Completion
|
|
|
|
If an Observable used in `toSignal` produces an error, that error is thrown when the signal is read.
|
|
|
|
If an Observable used in `toSignal` completes, the signal continues to return the most recently emitted value before completion.
|
|
|
|
## `toObservable`
|
|
|
|
The `toObservable` utility creates an `Observable` which tracks the value of a signal. The signal's value is monitored with an `effect`, which emits the value to the Observable when it changes.
|
|
|
|
```ts
|
|
import { Component, signal } from '@angular/core';
|
|
|
|
@Component(...)
|
|
export class SearchResults {
|
|
query: Signal<string> = inject(QueryService).query;
|
|
query$ = toObservable(this.query);
|
|
|
|
results$ = this.query$.pipe(
|
|
switchMap(query => this.http.get('/search?q=' + query ))
|
|
);
|
|
}
|
|
```
|
|
|
|
As the `query` signal changes, the `query$` Observable emits the latest query and triggers a new HTTP request.
|
|
|
|
### Injection context
|
|
|
|
`toObservable` by default needs to run in an injection context, such as during construction of a component or service. If an injection context is not available, an `Injector` can instead be explicitly specified.
|
|
|
|
### Timing of `toObservable`
|
|
|
|
`toObservable` uses an effect to track the value of the signal in a `ReplaySubject`. On subscription, the first value (if available) may be emitted synchronously, and all subsequent values will be asynchronous.
|
|
|
|
Unlike Observables, signals never provide a synchronous notification of changes. Even if your code updates a signal's value multiple times, effects which depend on its value run only after the signal has "settled".
|
|
|
|
```ts
|
|
const obs$ = toObservable(mySignal);
|
|
obs$.subscribe(value => console.log(value));
|
|
|
|
mySignal.set(1);
|
|
mySignal.set(2);
|
|
mySignal.set(3);
|
|
```
|
|
|
|
Here, only the last value (3) will be logged.
|