Основы RxJS для Angular
RxJS — библиотека для работы с потоками событий, она устанавливается вместе с Angular. HttpClient, маршрутизатор и формы возвращают объекты Observable из RxJS, поэтому немного знать её нужно. Честно: RxJS большая и непростая, но для обычного приложения хватает пяти понятий из этого урока, а состояние компонентов удобнее держать в сигналах.
Observable и subscribe
Observable — источник, который может выдать одно или несколько значений со временем, как Promise, который умеет отвечать больше одного раза. Запрос HttpClient выдаёт одно значение — ответ сервера — и завершается.
// src/app/post-list/post-list.ts
this.api.getPosts().subscribe({
next: (posts) => this.posts.set(posts),
error: (err) => console.error('Ошибка', err),
complete: () => console.log('Готово'),
});next получает значение, error — ошибку, complete вызывается в конце. Чаще всего нужен только next, и его передают просто функцией, как в прошлом уроке.
pipe и операторы
Метод pipe() пропускает значения через операторы — функции, которые их преобразуют. Операторы импортируют из rxjs:
// src/app/post-api.ts
import { map } from 'rxjs';
getTitles() {
return this.http.get<Post[]>(this.url).pipe(
map((posts) => posts.slice(0, 5)),
);
}Метод вернёт Observable, который выдаст только первые пять статей. Компонент подписывается на него так же, как раньше.
| Оператор | Что делает |
|---|---|
map | Преобразует значение |
filter | Пропускает только подходящие значения |
tap | Выполняет действие, не меняя значение (например, лог) |
catchError | Перехватывает ошибку и подставляет запасное значение |
switchMap | Переключается на новый Observable, отменяя прошлый |
Пайп async
Можно не подписываться вручную, а отдать Observable шаблону. Пайп async сам подпишется и отпишется:
// src/app/post-list/post-list.ts
import { Component, inject } from '@angular/core';
import { AsyncPipe } from '@angular/common';
import { PostApi } from '../post-api';
@Component({
selector: 'app-post-list',
imports: [AsyncPipe],
templateUrl: './post-list.html',
})
export class PostList {
posts$ = inject(PostApi).getPosts();
}<!-- src/app/post-list/post-list.html -->
@if (posts$ | async; as posts) {
@for (post of posts; track post.id) {
<h3>{{ post.title }}</h3>
}
} @else {
<p>Загрузка...</p>
}Знак доллара в конце имени — принятое обозначение Observable. Пока ответа нет, async возвращает null, и показывается «Загрузка...».
toSignal
Функция toSignal() превращает Observable в сигнал — так данные из HTTP читаются в шаблоне как любой сигнал:
// src/app/post-list/post-list.ts
import { Component, inject } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { PostApi } from '../post-api';
@Component({
selector: 'app-post-list',
templateUrl: './post-list.html',
})
export class PostList {
posts = toSignal(inject(PostApi).getPosts(), { initialValue: [] });
}В шаблоне пишут posts(), как в уроке о загрузке данных. Подписка и отписка происходят автоматически.
Частая ошибка
Подписаться вручную на долгий поток, например на изменения параметров маршрута, и не отписаться. Подписка живёт после удаления компонента и продолжает работать. Используйте async, toSignal или оператор takeUntilDestroyed() из @angular/core/rxjs-interop — они отписываются сами.