You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 17函数式拦截器控制进度条不显示问题求助

Angular函数式拦截器控制进度条不显示问题排查与修复

问题概述

使用Angular函数式Http拦截器,通过DataService中的BehaviorSubject控制进度条的显示与隐藏。拦截器能正常处理请求,但发起HTTP请求时进度条始终不显示,怀疑拦截器中注入的DataService是全新实例,与组件中使用的实例不一致。

相关代码

DataService 完整实现(需确保单例)

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

@Injectable({
  providedIn: 'root' // 核心:标记为根级单例服务
})
export class DataService {
  public isLoading = new BehaviorSubject<boolean>(false);
  isLoading$ = this.isLoading.asObservable();
}

拦截器代码

import { HttpEvent, HttpRequest, HttpHandlerFn, HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from './auth.service';
import { Observable, throwError } from 'rxjs';
import { mergeMap, catchError, finalize } from 'rxjs/operators';
import { DataService } from './data.service';

const settings = require('../configuration/settings?v=1.0.0');

export const Interceptor: HttpInterceptorFn = (
  req: HttpRequest<any>,
  next: HttpHandlerFn
): Observable<HttpEvent<any>> => {

  const _data = inject(DataService);
  const authService = inject(AuthService);

  // 标记请求开始
  _data.isLoading.next(true);

  return authService.getAccessTokenSilently$().pipe(
    mergeMap(token => {
      const tokenReq = req.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`,
          VersionVerify: 'true',
          Version: settings.version,
          'Cache-Control': 'no-cache',
          Pragma: 'no-cache'
        }
      });

      return next(tokenReq).pipe(
        catchError(err => {
          if (err?.error?.message?.match(/^Version:/)) {
            const data = err.error.data;
            if (data?.version) {
              console.log(`Version (ours)|(curr):  (${settings.version})|(${data.version})`);
            }

            const versionReq = req.clone({
              setHeaders: {
                Authorization: `Bearer ${token}`,
                'Cache-Control': 'no-cache',
                Pragma: 'no-cache'
              }
            });

            return next(versionReq);
          }
          return throwError(() => err);
        })
      );
    }),
    catchError(err => throwError(() => err)),
    // 无论请求成功/失败、重试与否,最终都标记请求结束
    finalize(() => _data.isLoading.next(false))
  );
};

app.component.html 代码

<div class="flex flex-column">
  @if(_dataService.isLoading$ | async) {
    <p-progressBar mode="indeterminate" [style]="{ height: '6px' }"></p-progressBar>
  }
  <app-top-menu></app-top-menu>
  <p-toast></p-toast>
  <router-outlet></router-outlet>
</div>

问题排查与修复步骤

1. 确保DataService是单例

这是最可能的核心问题:如果DataService未配置为单例,拦截器和组件会各自获得独立实例,导致组件订阅的isLoading$无法收到拦截器的状态更新。

  • 必须在DataService上添加@Injectable({ providedIn: 'root' }),或者在根模块(AppModule)的providers数组中声明该服务,禁止在多个模块/组件中重复提供。

2. 修复进度条状态更新逻辑

原代码仅在请求成功的map操作中设置isLoading.next(false),存在多处漏洞:

  • 获取token失败时,状态永远不会重置为false
  • 版本验证重试的请求结束后,未触发状态重置
  • map仅响应HttpResponse事件,无法覆盖请求出错、取消等场景

修改后使用finalize操作符,确保所有分支的请求结束后都会重置isLoading状态。

3. 组件中正确注入DataService

确保AppComponent中正确注入并暴露DataService给模板使用:

import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(public _dataService: DataService) {} // 公开属性以便模板访问
}

4. 可选:并发请求优化(进阶)

如果存在多个并发HTTP请求,单个请求结束就重置isLoading会导致进度条提前消失。可以扩展DataService用计数器跟踪请求数量:

@Injectable({ providedIn: 'root' })
export class DataService {
  private loadingCount = 0;
  public isLoading = new BehaviorSubject<boolean>(false);
  isLoading$ = this.isLoading.asObservable();

  startLoading(): void {
    this.loadingCount++;
    this.isLoading.next(true);
  }

  stopLoading(): void {
    this.loadingCount--;
    if (this.loadingCount <= 0) {
      this.loadingCount = 0;
      this.isLoading.next(false);
    }
  }
}

拦截器中替换为_data.startLoading()和_data.stopLoading()即可。

内容的提问来源于stack exchange,提问作者CStreet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 19:20:05