在 Angular7 中的 HTTP 请求期间显示微调器

Showing a spinner during HTTP requests in Angular7

我试图在 Angular 7 上的应用程序的 HTTP 请求期间显示微调器。 我已经创建了一个 HttpInterceptor class、一个加载器服务和一个加载器组件以及一个表示微调器状态(显示或隐藏)的 Subject。但是加载器服务和加载器组件之间似乎存在问题

HttpInterceptor 似乎工作正常,因为控制台输出 true 和 false 状态(下面的代码),但是加载程序服务和加载程序组件似乎有问题。我使用 ngIf 指令根据 Subject 的状态显示或隐藏微调器。

加载服务:

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

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

  isLoading = new Subject<boolean>();

  show() {
    console.log('true');
    this.isLoading.next(true);
  }

  hide() {
    console.log('false');
    this.isLoading.next(false);
  }

  constructor() { }
}

LoaderComponent

import { Component, OnInit } from '@angular/core';
import {LoaderService} from '../loader.service';
import { Subject } from 'rxjs';

@Component({
  selector: 'app-loader',
  templateUrl: './loader.component.html',
  styleUrls: ['./loader.component.css']
})
export class LoaderComponent implements OnInit {

  isLoading: Subject<boolean> = this.loaderService.isLoading;

  constructor(private loaderService: LoaderService) { }

  ngOnInit() {
  }

}
<div *ngIf="isLoading | async " class="d-flex justify-content-center">
  <div class="spinner-border" role="status">
    <span class="sr-only">Loading...</span>
  </div>
</div>

目前微调器根本没有显示或隐藏。我只在加载开始时在控制台中得到 'true',然后在加载结束时得到 'false'。 我已将加载器选择器包含在 app.component.html 文件中。它应该工作正常。我是不是做错了什么?

您应该使用 BehaviourSubject 而不是 SubjectSubject 仅在调用 next() 时发出值 - 如果在服务推送值后初始化组件,则不会收到任何内容。

BehaviourSubject 通过记住最后发出的值来解决这个问题。因此,当您的模板订阅时,它可以获得最后一个值并进行比较。

创建一个可以发出 HTTP 请求的布尔状态的 HttpInterceptor 非常棘手。可以同时发生多个并行请求,这并不像为每个 HttpRequest 发出 true/false 那么简单。您只需要在请求开始时发出 true 一次,而在 last 请求完成时发出 false

最简单的方法是跟踪并发请求数。

这是我使用的拦截器。它仅在启动新请求时发出 true,并且仅在所有请求完成时发出 false。

import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {Observable, Subject} from 'rxjs';
import {distinctUntilChanged, finalize, map} from 'rxjs/operators';

class BusySubject extends Subject<number> {
    private _previous: number = 0;

    public next(value: number) {
        this._previous += value;
        if (this._previous < 0) {
            throw new Error('unexpected negative value');
        }
        return super.next(this._previous);
    }
}

@Injectable()
export class BusyInterceptor implements HttpInterceptor {

    private _requests: BusySubject = new BusySubject();

    public get busy(): Observable<boolean> {
        return this._requests.pipe(
            map((requests: number) => Boolean(requests)),
            distinctUntilChanged(),
        );
    }

    public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this._requests.next(1);
        return next.handle(req).pipe(finalize(() => this._requests.next(-1)));
    }
}

p.s. This could properly be done using a scan() operator, but I haven't had time to update the code.