[RxJS] Reactive pattern: Passing Observable and trigger side effect until it complete

For example, we want to show loading spinner inside our appliction component trees. Because we might trigger loading spinner anywhere inside our application, therefore, we need to use a loading.service.ts to communciate between different compnents.

loading.service.ts:

import {Injectable} from '@angular/core';
import {BehaviorSubject, Observable, of} from 'rxjs';
import {concatMap, finalize, tap} from 'rxjs/operators';

@Injectable()
export class LoadingService {

  private loadingSubject = new BehaviorSubject<boolean>(false);

  loading$: Observable<boolean> = this.loadingSubject.asObservable();

  loadingOn() {
    this.loadingSubject.next(true);
  }

  loadingOff() {
    this.loadingSubject.next(false);
  }

  showLoaderUntilCompleted<T>(obs$: Observable<T>): Observable<T> {
    return of(null)
      .pipe(
        tap(() => this.loadingSubject.next(true)),
        concatMap(() => obs$),
        finalize(() => {
          this.loadingSubject.next(false);
        })
      );
  }
}

Component:

import { Component, OnInit } from '@angular/core';
import {Observable} from 'rxjs';
import {LoadingService} from './loading.service';

@Component({
  selector: 'loading',
  template: '<div class="spinner-container" *ngIf="loading$ | async"><mat-spinner></mat-spinner></div>'
})
export class LoadingComponent implements OnInit {

  loading$ : Observable<boolean>;
  constructor(private loadingService: LoadingService) {

  }

  ngOnInit() {

    this.loading$ = this.loadingService.loading$;
  }
}

 

The most important thing to understand here is the function: showLoaderUntilComplete(obs$). 

It takes a Observable, because we want to wait 'obs$' to complete, therefore, we use 'concatMap', it executes observable one by one, then we use 'finalize' operator, it will execute callback function when the observable completes. Here means waiting 'obs$' to be completed.

 

How to use it?

courses.service.ts

private subject = new BehaviorSubject<Course[]>([]);

courses$ = this.subject.asObservable();  

private loadAllCourses() {

      const loadCourses$ =  this.http.get<Course[]>('/api/courses')
        .pipe(
          map(response => response["payload"])
        );

      this.loading.showLoaderUntilCompleted(loadCourses$)
        .subscribe(
          courses => this.subject.next(courses)
        );
  }

 

posted @ 2020-03-07 00:20  Zhentiw  阅读(412)  评论(0编辑  收藏  举报