We use cookies to enhance your experience on the site
CodeWorlds

Subjects - Multi-directional Communication

Subjects are special Observables that can both emit and receive values - like a ninja command center.

Types of Subjects

1import { Subject, BehaviorSubject, ReplaySubject, AsyncSubject } from 'rxjs';
2
3// Subject - basic, no memory
4const subject = new Subject<string>();
5subject.subscribe(v => console.log('A:', v));
6subject.next('Hello');  // A: Hello
7subject.subscribe(v => console.log('B:', v));
8subject.next('World');  // A: World, B: World
9// B did not receive 'Hello' - there was no subscription
10
11// BehaviorSubject - remembers the last value
12const behavior = new BehaviorSubject<number>(0);  // Requires initial value
13behavior.subscribe(v => console.log('A:', v));  // A: 0 (immediately!)
14behavior.next(1);  // A: 1
15behavior.subscribe(v => console.log('B:', v));  // B: 1 (last value!)
16behavior.next(2);  // A: 2, B: 2
17
18// ReplaySubject - remembers the last n values
19const replay = new ReplaySubject<number>(2);  // Remembers last 2
20replay.next(1);
21replay.next(2);
22replay.next(3);
23replay.subscribe(v => console.log(v));  // 2, 3 (last 2)
24
25// AsyncSubject - emits only the last value on complete
26const async = new AsyncSubject<number>();
27async.next(1);
28async.next(2);
29async.subscribe(v => console.log(v));  // Nothing
30async.next(3);
31async.complete();  // Now: 3

Practical Example - State Service

1@Injectable({ providedIn: 'root' })
2export class NotificationService {
3  private notificationsSubject = new BehaviorSubject<Notification[]>([]);
4
5  // We expose only the Observable (readonly)
6  readonly notifications$ = this.notificationsSubject.asObservable();
7
8  // Current value available synchronously
9  get currentNotifications(): Notification[] {
10    return this.notificationsSubject.getValue();
11  }
12
13  addNotification(notification: Notification): void {
14    const current = this.currentNotifications;
15    this.notificationsSubject.next([...current, notification]);
16  }
17
18  removeNotification(id: number): void {
19    const filtered = this.currentNotifications.filter(n => n.id !== id);
20    this.notificationsSubject.next(filtered);
21  }
22
23  clearAll(): void {
24    this.notificationsSubject.next([]);
25  }
26}
27
28// Usage in a component
29@Component({
30  template: \`
31    @for (notification of notifications$ | async; track notification.id) {
32      <div class="notification" (click)="remove(notification.id)">
33        {{ notification.message }}
34      </div>
35    }
36  \`
37})
38export class NotificationListComponent {
39  notifications$ = inject(NotificationService).notifications$;
40  private notificationService = inject(NotificationService);
41
42  remove(id: number): void {
43    this.notificationService.removeNotification(id);
44  }
45}
Go to CodeWorlds