【问题标题】:delayed Angular spinner without flickering没有闪烁的延迟角微调器
【发布时间】:2020-04-02 07:21:58
【问题描述】:

目前我正在尝试使用 Angular HttpInterceptor 来显示一个微调器一次,因此它不会在每次 HttpRequest 完成时闪烁。我的interceptor.service.ts 的当前代码如下所示:

@Injectable()
export class InterceptorService implements HttpInterceptor {
  constructor(private spinnerService: SpinnerService) {}

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

  hideSpinner() {
    this.spinnerService.show.next(false);
    console.log('false');
  }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler,
  ): Observable<HttpEvent<any>> {
    const responseTimer$ = next
      .handle(req)
      .pipe(filter(e => e instanceof HttpResponse));
    timer(300)
      .pipe(takeUntil(responseTimer$))
      .subscribe(() => this.showSpinner());

    return next.handle(req).pipe(
      tap(evt => {
        if (evt instanceof HttpResponse) {
          this.hideSpinner();
        }
      }),
    );
  }
}

但是如果我尝试这段代码,控制台会说:

interceptor.service.ts:20 true
interceptor.service.ts:25 false
4 x interceptor.service.ts:20 true
4 x interceptor.service.ts:25 false
5 x interceptor.service.ts:25 false
interceptor.service.ts:25 false

但在我看来,它应该是正确的 x 次,最后是错误的。我希望你能理解我想要归档的内容。

【问题讨论】:

    标签: javascript angular typescript spinner angular-http-interceptors


    【解决方案1】:

    您的拦截器服务中需要一个计数器,用于显示待处理请求的数量。当数字>0时,您会显示您的微调器。


    编辑:

    文本编辑器的理论

    @Injectable()
    export class InterceptorService implements HttpInterceptor {
      pendingCount$: BehaviorSubject<number> = new BehaviorSubject(0);
    
      constructor(private spinnerService: SpinnerService) {
        this.pendingCount$.pipe(
          map(Boolean),
          distinctUntilChanged(),
        ).subscribe(
          show => this.spinnerService.show.next(show),
        );
      }
    
      intercept(
        req: HttpRequest<any>,
        next: HttpHandler,
      ): Observable<HttpEvent<any>> {
    
        this.pendingCount$.next(this.pendingCount$.value++);
    
        return next.handle(req).pipe(
          tap(evt => this.pendingCount$.next(this.pendingCount$.value--)),
        );
      }
    }
    

    【讨论】:

    • 我喜欢这个解决方案。这很简单,但很有效。
    • 这听起来是个不错的解决方案。您能否提供信息,我将在哪里/如何在此代码中实现未决请求的计数器?
    • 您可以将该信息存储在拦截器中,因为它是一个单例服务
    • 但是我什么时候必须增加/减少计数器?我还注意到,takeUntil(responseTimer$)doubles OPTIONS-Request。因此,如果我不评论该行,我的后端会认为它第二次被相同的请求调用。
    • 可以过滤options方法。您现在增加和减少显示和隐藏微调器的位置
    【解决方案2】:

    创建自己的 Subject 会更容易,它会跟踪已发出的请求数量,然后如果跟踪计数低于零,则通过抛出错误来保护自己免受错误。

    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)));
        }
    }
    

    在上面需要注意的是,我使用distinctUntilChanged() 作为繁忙指示器,同时使用finalize() 作为减少触发器。这些对于跟踪正确的忙碌状态很重要。

    上述应该可以工作,无论何时您订阅忙碌。因为_previous 值是主体的内部状态。即使没有订阅任何内容,它也应该继续正确跟踪。它的工作方式有点像BehaviorSubject(),只是它使用+= 来应用更改。

    【讨论】:

    • 仅供参考:我用你的拦截器替换了我的拦截器服务。在那之后,我只是将this.busy.subscribe(b =&gt; this.spinnerService.show.next(b)); 添加到服务中,它就像一个魅力。谢谢你们!
    猜你喜欢
    • 1970-01-01
    • 2014-07-12
    • 2016-02-28
    • 2020-05-24
    • 1970-01-01
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 2017-04-11
    相关资源
    最近更新 更多