Skip to content
Angular Essentials
GitHub

Subject

  • The subject is a special type of Observable that allows values to be multicasted to many Observers.
  • While plain Observables are unicast (each subscribed Observer owns an independent execution of the Observable), Subjects are multicast.
  • Every Subject is an Observable. Given a Subject, you can subscribe to it, providing an Observer, which will start receiving values normally.
  • From the perspective of the Observer, it cannot tell whether the Observable execution is coming from a plain unicast Observable or a Subject.
  • Every Subject is an Observer.
  • It is an object with the methods next(v), error(e), and complete().
  • To feed a new value to the Subject, just call next(theValue), and it will be multicasted to the Observers registered to listen to the Subject.
  • Subject is a hot observable.

Using subject in our application

For authentication mock

import {Injectable} from '@angular/core';
import {BehaviorSubject} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  isLoggedIn$$ = new BehaviorSubject<boolean>(false);
}
<!--header.component.html-->
<mat-toolbar color="primary">
  ...
  <button *ngIf="!isLoggedIn" (click)="login()" mat-icon-button aria-label="login">
    <mat-icon>login</mat-icon>
  </button>
  <button *ngIf="isLoggedIn" (click)="logout()" mat-icon-button aria-label="login">
    <mat-icon>logout</mat-icon>
  </button>
</mat-toolbar>

takeUntil(this.unsubscribe) basically means consume isLoggedIn$$ until this.unsubscribe is complete and we complete this.unsubcsribe when this component is destroyed. This is an example of playing around RxJS operators. You could have used Subscription object to store subscription and unsubsribe later.

export class HeaderComponent implements OnInit, OnDestroy {
  
  isLoggedIn!: boolean;

  private unsubscribe = new Subject<void>(); // <-- subject which emits void

  constructor(private readonly authService: AuthService) {}

  ngOnInit(): void {
    this.authService.isLoggedIn$$
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(val => this.isLoggedIn = val);
  }

  login(): void {
    this.authService.isLoggedIn$$.next(true);
  }

  logout(): void {
    this.authService.isLoggedIn$$.next(false);
  }

  ngOnDestroy() {
    this.unsubscribe.next();
    this.unsubscribe.complete();
  }
}
export const authGuard = () => {
    const authService = inject(AuthService);
    const router = inject(Router);
    console.log('AuthGuard called');
    return authService.isLoggedIn$$.value ? true : router.navigate(['']);
};