等待一段时间后显示微调框

时间:2018-07-31 08:27:44

标签: angular angular2-observables

我的Angular应用程序执行很多Http请求,因此我想显示一个微调框,以通知用户该应用程序正在运行(并且不是“冻结”)。因此,我实现了以下拦截器:

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

@Injectable()
export class LoadingInterceptor implements HttpInterceptor {

  readonly TIMEOUT_VALUE = 10000;
  private requests: number = 0;

  constructor() {
  }

  public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.addRequest(request);
    return next.handle(request)
      .pipe(
        timeout(this.TIMEOUT_VALUE),
        finalize(() => this.removeRequest(request))
      );
  }

  private addRequest(request: HttpRequest<any>) {
    this.requests++;
    if (this.requests === 1)
      this.present();
  }

  private present() {
    // show the spinner
  }

  private removeRequest(request: HttpRequest<any>) {
    this.requests--;
    if (this.requests === 0)
      this.dismiss();
  }

  private dismiss() {
    // dismiss the spinner
  }

}

以上代码在有待处理的请求时显示微调框,并在所有请求完成后将其关闭。但是,请求通常会在不到一秒的时间内返回一个值,因此,我只想在请求花费一秒以上的时间才能显示微调框。

我的第一种方法是在构造函数中有一个间隔,该间隔每秒检查一次挂起的请求数,并相应地显示/关闭微调器。

constructor() {
  Observable.interval(1000).subscribe(() => {
    if (this.requests === 1)
      this.present();
    if (this.requests === 0)
      this.dismiss();
  });
}

但是这对我来说似乎很难看。有没有更优雅的方式来完成此任务?

2 个答案:

答案 0 :(得分:0)

我已经使用mapdistinctUntilChangeddebounce运算符来实现这一目标。

map将计数器值转换为布尔值,以便我们仅处理“ on” /“ off”值。

distinctUntilChanged丢弃重复项,因此微调框仅接收其状态的更改。

debounce处理显示微调器的1秒钟延迟。

请参阅this StackBlitz demo以及模拟的请求计数器序列。

答案 1 :(得分:0)

遇到同样的问题。找不到好的解决方案,而是通过debounceTime和counter完成的。

计数器已用于多个请求。

HttpLoadingInterceptor

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

    import { LoadingIndicatorService } from '@app/services/loading-indicator.service';

    @Injectable()
    export class HttpLoadingInterceptor implements HttpInterceptor {
      constructor(public service: LoadingIndicatorService) {
      }

      intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.service.show();

        return next.handle(request).pipe(
          finalize(() => this.service.hide())
        );
      }
    }

LoadingIndicatorService

import { Injectable, EventEmitter } from '@angular/core';
import { Subject } from 'rxjs';
import { debounceTime, } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class LoadingIndicatorService {
  private counter = 0;
  private delay = 700;
  private isLoading = new Subject<boolean>();

  public stateChanged = new EventEmitter<boolean>();

  constructor() {
    this.isLoading.pipe(
      debounceTime(this.delay)
    ).subscribe(value => {
      if (value)
        this.counter++;
      else if (this.counter > 0) {
        this.counter--;
      }

      this.stateChanged.emit(this.counter > 0);
    });
  }

  public show() {
    this.isLoading.next(true);
  }
  public hide() {
    this.isLoading.next(false);
  }
}

SpinnerComponent

import { Component, OnInit, AfterViewInit, OnDestroy } from '@angular/core';
import { LoadingIndicatorService } from '@app/services/loading-indicator.service';

@Component({
  selector: 'app-spinner',
  templateUrl: './spinner.component.html',
  styleUrls: ['./spinner.component.scss']
})
export class SpinnerComponent implements AfterViewInit, OnDestroy {
  isBusy = false;
  subscription = null;

  constructor(
    private loadingIndicatorSvc: LoadingIndicatorService) {
  }

  ngAfterViewInit() {
    this.subscription = this.loadingIndicatorSvc.stateChanged
      .subscribe(value => {
        if (this.isBusy !== value) {
          this.isBusy = value;

          document.body.style.overflow = this.isBusy
            ? 'hidden'
            : 'auto';
        }
      });
  }

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }
}