Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Основы 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 — они отписываются сами.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.