Search code examples
javascriptangulartypescriptobservablereactivex

Angular 6 Subject Subscribe is not working


I have an http interceptor which emits a 'string' whenever a request starts and end:

@Injectable({
  providedIn: 'root'
})
export class LoadingIndicatorService implements HttpInterceptor {

  private loadingIndicatorSource = new  Subject<string>();
  private loadingIndicator$ = this.loadingIndicatorSource.asObservable();

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.updateVisibility('block');
    return next.handle(req)
      .pipe(
        finalize(
          () => {
            this.updateVisibility('none');
          }
        )
      )
      ;
  }

  updateVisibility(state: string) {
    this.loadingIndicatorSource.next(state);
  }

  getLoadingIndicator() {
    return this.loadingIndicator$;
  }
}

This is the component where I injected the service:

export class AppComponent {


  display = 'none';

  constructor(public  authenticationService: AuthenticationService,
              public loadingIndicatorService: LoadingIndicatorService) {
    this.loadingIndicatorService.getLoadingIndicator().subscribe(visibility => {
      console.log(visibility);
      this.display = visibility;
    });

    }
  }

Actually I am trying to show a loading indicator:

 <div [style.display]="display">
    <mat-progress-bar mode="indeterminate" color="warn" ></mat-progress-bar>
  </div>

I flowed this tutorial from the official Angular site.

But the subscribe method never executes.

Why subscribe method is not working?


Solution

  • What i would do is create a SpinnerService and a SpinnerInterceptor and not merge them together.

    Make the spinner service allocate the amount of outstanding requests and show the spinner if the outstanding requests are greater than 0.

    @Injectable()
    export class SpinnerService {
        private requestAmount$ = new BehaviorSubject(0);
    
    public showSpinner$ = this.requestAmount$.asObservable().pipe(map(r => r > 0));
    
    requestStart() {
        this.requestAmount$.next(this.requestAmount$.getValue() + 1);
    }
    
    requestEnd() {
        this.requestAmount$.next(this.requestAmount$.getValue() - 1);
    }
    }
    

    Inside your spinnerInterceptor you can inject the SpinnerService and update it based on each request.

    @Injectable()
    export class SpinnerInterceptor implements HttpInterceptor {
        constructor(private spinnerService: SpinnerService) {}
    
    intercept(req: HttpRequest<any>, next: HttpHandler) {
            this.spinnerService.requestStart();
    
            return next.handle(req).pipe(
                finalize(() => {
                    this.spinnerService.requestEnd();
                })
            );
        }
    }
    

    Inside your app component use the ngOnInit hook do not use the constructor it is against the style guide.

    export class AppComponent implements OnInit{
    
    
      display = 'none';
    
      constructor(public  authenticationService: AuthenticationService,
                  public spinnerService: SpinnerService) {}
    ngOnInit(){
        this.spinnerService.showSpinner$.subscribe(visibility => {
          console.log(visibility);
          this.display = visibility ? 'block': 'none';
        });
    
        }
      }
    

    Separation makes it easier to read and understand.

    Hope this helped!