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
相关产品推荐
相关产品推荐

