我的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();
});
}
但是这对我来说似乎很难看。有没有更优雅的方式来完成此任务?
答案 0 :(得分:0)
我已经使用map
,distinctUntilChanged
和debounce
运算符来实现这一目标。
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();
}
}