Angular IDPStrong Junior
🔥 0

Модуль 4 · урок 8 з 13

Subjects

Subject — це одночасно потік і спосіб у нього надсилати. Саме він перетворює RxJS з інструменту читання даних на інструмент керування станом. У таблиці ліда згадані BehaviorSubject і ReplaySubject — розберемо всі чотири, щоб різниця була очевидною.

Subject: і мікрофон, і динамік

import { Subject } from 'rxjs';

const refresh$ = new Subject<void>();

refresh$.subscribe(() => console.log('оновлюємось'));
refresh$.next();     // сам штовхаєш значення в потік

На відміну від of чи http.get, Subject гарячий: він живе сам по собі, а підписники отримують лише те, що прийшло після їхньої підписки. Підписався пізно — попереднє пропустив.

Чотири види

ПОТІК ВИДАВ 1, 2, 3 — ПОТІМ ПРИЙШОВ НОВИЙ ПІДПИСНИК Subject підписка отримає нічого — лише майбутні BehaviorSubject одразу отримає 3 — поточне значення ReplaySubject(2) отримає 2 і 3 — буфер останніх AsyncSubject віддає лише останнє значення, і тільки після complete — використовується рідко.
Різниця тільки в тому, що бачить підписник, який приєднався пізно. Для стану це вирішальна деталь.

BehaviorSubject — стандарт для стану

import { BehaviorSubject } from 'rxjs';

private readonly _filters = new BehaviorSubject<Filters>({ status: 'all', query: '' });

readonly filters$ = this._filters.asObservable();   // назовні лише для читання

get filters(): Filters {
  return this._filters.value;    // поточне значення без підписки
}

setStatus(status: TaskStatus | 'all'): void {
  this._filters.next({ ...this._filters.value, status });   // незмінне оновлення
}

Три причини, чому саме він:

  • Завжди має значення — початкове задається в конструкторі, тож підписник ніколи не побачить порожнечі.
  • Новий підписник одразу отримує поточне — компонент, який відкрився пізніше, бачить актуальний стан, а не чекає наступної зміни.
  • Значення можна прочитати синхронно через .value — зручно, коли треба щось порахувати в обробнику події.

Приватний Subject, публічний Observable

// ❌ так будь-хто ззовні може штовхнути що завгодно
readonly tasks$ = new BehaviorSubject<Task[]>([]);

// ✅ так змінювати можна лише через методи сервісу
private readonly _tasks = new BehaviorSubject<Task[]>([]);
readonly tasks$ = this._tasks.asObservable();

Це той самий принцип інкапсуляції, що й з signal і asReadonly() з модуля 2. Зовні — контракт на читання, всередині — контрольовані зміни. Наступний урок цілком побудований на цьому патерні.

Коли який брати

ВидЩо бачить пізній підписникТиповий випадок
SubjectНічого з минулогоПодії: «оновити», «закрити модалку»
BehaviorSubjectПоточне значенняСтан: фільтри, дані, поточний користувач
ReplaySubject(n)Останні n значеньІсторія, лог подій, кеш кількох останніх
AsyncSubjectЛише останнє, після completeМайже ніколи
Subject чи сигнал

У сучасному Angular для локального стану компонента здебільшого беруть signal. BehaviorSubject лишається доречним там, де стан треба комбінувати з іншими потоками — з debounceTime, switchMap, HTTP. Тобто в сервісах-сторах, де живе асинхронність. У таблиці ліда згаданий саме він, тож у практиці модуля робимо на ньому — а в уроці 12 подивимось, як їх зшивати між собою.