Search code examples
angularrxjs

Angular finalize is not being called when using Behaviour Subject


I want to create a spinner every time user id is changed. The content must change on the same page. But the finalize operator is not running and the spinner is forever spinning. Is there an another aproach?

<button (click)="setUserId(1)">USER 1</button>
<button (click)="setUserId(2)">USER 2</button>

<section id="user-courses" *ngIf="!spinner.userCourses"></section>
<div *ngIf="spinner.userCourses">
    LOADING...
</div>

<section id="user-songs" *ngIf="!spinner.userSongs"></section>
<div *ngIf="spinner.userSongs">
    LOADING...
</div>

<section id="user-address" *ngIf="!spinner.userAddress"></section>
<div *ngIf="spinner.userAddress">
    LOADING...
</div>

public user$: BehaviorSubject<number | null> = new BehaviorSubject(null);

setUserId(id: number) {
  this.user$.next(id);
}

getUserId() {
  return this.user$.asObservable();
}

public ngOnInit(): void {
  this.getUserId().pipe(
    tap(r => this.spinner.userCourses = true),
    switchMap(userId => {
      this.courseService.getUserCourse(userId);
    }),
    finalize(() => this.spinner.userCourses = false)
  ).subscribe()

  this.getUserId().pipe(
    tap(r => this.spinner.userAddress = true),
    switchMap(userId => {
      this.addressService.getUserAddress(userId);
    }),
    finalize(() => this.spinner.userAddress = false)
  ).subscribe()

  this.getUserId().pipe(
    tap(r => this.spinner.userSongs = true),
    switchMap(userId => {
      this.songService.getUserFavoriteSongs(userId);
    }),
    finalize(() => this.spinner.userSongs = false)
  ).subscribe()
}

Solution

  • Since user$ never completes, finalize will never be executed. You could instead put the finalize call on the inner http call:

      this.user$.pipe(
        tap(r => this.spinner.userCourses = true),
        switchMap(userId => this.courseService.getUserCourse(userId).pipe(
          finalize(() => this.spinner.userCourses = false)        
        )),
      ).subscribe();
    

    Alternatively, you could use an additional tap instead of finalize as suggested by @JohnMontgomery:

      this.user$.pipe(
        tap(r => this.spinner.userCourses = true),
        switchMap(userId => this.courseService.getUserCourse(userId))       
        tap(() => this.spinner.userCourses = false) 
      ).subscribe();