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(['']);
};