Kurs Angular · Moduł 7: RxJS

Subjects - Wielokierunkowa Komunikacja

2 min czytania ·
W tej lekcji2

Subjects to specjalne Observable, które mogą zarówno emitować jak i odbierać wartości - jak centrum dowodzenia ninja.

Typy Subjects

import { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';

// Subject - podstawowy, bez pamięci
const subject = new Subject<string>();
subject.subscribe(v => console.log('A:', v));
subject.next('Hello');  // A: Hello
subject.subscribe(v => console.log('B:', v));
subject.next('World');  // A: World, B: World
// B nie otrzymał 'Hello' - nie było subskrypcji

// BehaviorSubject - pamięta ostatnią wartość
const behavior = new BehaviorSubject<number>(0);  // Wymaga wartości początkowej
behavior.subscribe(v => console.log('A:', v));  // A: 0 (od razu!)
behavior.next(1);  // A: 1
behavior.subscribe(v => console.log('B:', v));  // B: 1 (ostatnia wartość!)
behavior.next(2);  // A: 2, B: 2

// ReplaySubject - pamięta n ostatnich wartości
const replay = new ReplaySubject<number>(2);  // Pamięta 2 ostatnie
replay.next(1);
replay.next(2);
replay.next(3);
replay.subscribe(v => console.log(v));  // 2, 3 (ostatnie 2)

// AsyncSubject - emituje tylko ostatnią przy complete
const async = new AsyncSubject<number>();
async.next(1);
async.next(2);
async.subscribe(v => console.log(v));  // Nic
async.next(3);
async.complete();  // Teraz: 3

Praktyczny przykład - Serwis Stanu

@Injectable({ providedIn: 'root' })
export class NotificationService {
  private notificationsSubject = new BehaviorSubject<Notification[]>([]);

  // Wystawiamy tylko Observable (readonly)
  readonly notifications$ = this.notificationsSubject.asObservable();

  // Bieżąca wartość dostępna synchronicznie
  get currentNotifications(): Notification[] {
    return this.notificationsSubject.getValue();
  }

  addNotification(notification: Notification): void {
    const current = this.currentNotifications;
    this.notificationsSubject.next([...current, notification]);
  }

  removeNotification(id: number): void {
    const filtered = this.currentNotifications.filter(n => n.id !== id);
    this.notificationsSubject.next(filtered);
  }

  clearAll(): void {
    this.notificationsSubject.next([]);
  }
}

// Użycie w komponencie
@Component({
  template: `
    @for (notification of notifications$ | async; track notification.id) {
      <div class="notification" (click)="remove(notification.id)">
        {{ notification.message }}
      </div>
    }
  `
})
export class NotificationListComponent {
  notifications$ = inject(NotificationService).notifications$;
  private notificationService = inject(NotificationService);

  remove(id: number): void {
    this.notificationService.removeNotification(id);
  }
}

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Czym różni się BehaviorSubject od zwykłego Subject?

  2. 2. Co charakteryzuje ReplaySubject?

To 2 z 4 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Edytor kodu

    Uzupełnij serwis stanu użytkownika: potrzebny jest Subject z wartością początkową, który pamięta ostatnią wartość i oddaje ją przez getValue().

  • Układanie w poziomie

    Ułóż tworzenie BehaviorSubject z wartością początkową 0:

  • Układanie w pionie

    Ułóż linie metody dodającej powiadomienie do serwisu opartego na BehaviorSubject:

Przydatne artykuły