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

Angular 17独立组件加载器拦截器失效及API调用报错排查

Angular 17 独立组件模式下加载器失效及「Cannot read properties of undefined (reading 'apply')」错误修复

问题根源

  • 服务实例隔离:LoadingComponent的组件元数据中添加了providers: [LoaderService],导致组件创建了独立的服务实例,和全局root级别的LoaderService状态无法同步。
  • RxJS 操作符版本不兼容:ProgressInterceptor使用了旧版RxJS的finally操作符,Angular 17配套的RxJS已改用finalize操作符,且导入方式错误。
  • AppComponent代码不完整:缺少Router注入、构造函数语法错误、未导入LoaderService。
  • 路由逻辑冗余:每次路由事件都触发startLoading,导致重复加载状态。

分步修复

1. 统一LoaderService实例

移除LoadingComponent中providers数组里的LoaderService,确保全局共享同一个服务实例:

// loading.component.ts
@Component({
  selector: 'loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.css'],
  standalone: true,
  imports: [CommonModule],
  // 移除该行:providers:[LoaderService]
})

2. 替换RxJS操作符

将finally改为新版finalize操作符,并修正导入路径:

// progress.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
// 替换旧导入,使用rxjs官方操作符
import { finalize } from 'rxjs/operators';
import { LoaderService } from '../service';

@Injectable()
export class ProgressInterceptor implements HttpInterceptor {
  constructor(private loaderSvc: LoaderService) {}
  
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (request.params.has('ignoreLoadingBar')) {
      return next.handle(request.clone({ headers: request.headers.delete('ignoreLoadingBar') }));
    }
    this.loaderSvc.startLoading();
    // 用finalize替代finally,通过pipe包裹
    return next.handle(request).pipe(
      finalize(() => this.loaderSvc.stopLoading())
    );
  }
}

3. 修复AppComponent代码缺陷

补充依赖注入,修正语法错误,优化路由事件逻辑:

// app.component.ts
import { Component, OnInit } from '@angular/core';
import { Router, NavigationEnd, RouterModule } from '@angular/router';
import { LoadingComponent } from './shared/components';
import { LoaderService } from './shared/service';
// 补充WINDOW令牌的导入路径
import { WINDOW } from './path-to-window-token';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  standalone: true,
  imports: [RouterModule, NgIdleKeepaliveModule, LoadingComponent],
  providers: [{ provide: WINDOW, useValue: window }] // 修正useValue为实际window对象
})
export class AppComponent implements OnInit {
  constructor(
    private router: Router,
    public loaderSVC: LoaderService
  ) {}

  ngOnInit() {
    this.router.events.subscribe((evt) => {
      // 仅在非NavigationEnd事件时启动加载,导航完成后停止
      if (evt instanceof NavigationEnd) {
        setTimeout(() => this.loaderSVC.stopLoading());
      } else {
        this.loaderSVC.startLoading();
      }
    });
  }
}

4. 验证拦截器配置

确认appConfig中HTTP_INTERCEPTORS配置正确,当前配置已满足要求,无需修改:

// app.config.server.ts
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideClientHydration(withHttpTransferCacheOptions({
      includePostRequests: true
    })),
    provideNgxMask(),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: ProgressInterceptor,
      multi: true
    },
    {
      provide: HTTP_INTERCEPTORS,
      useClass: BasicAuthInterceptor,
      multi: true
    },
    provideAnimations(),
    CookieService, 
    StorageService, 
    provideToastr({ positionClass: 'toast-top-right' }),
    importProvidersFrom(ReactiveFormsModule, HttpClientModule),
  ]
};

额外优化:避免内存泄漏

使用takeUntil操作符替代手动取消订阅,简化代码同时防止内存泄漏:

// loading.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { LoaderService } from '../../service';
import { Subject, takeUntil } from 'rxjs';
import { LoaderState } from '../../models/loader.model'
import { CommonModule } from '@angular/common';

@Component({
  selector: 'loading',
  templateUrl: './loading.component.html',
  styleUrls: ['./loading.component.css'],
  standalone: true,
  imports: [CommonModule]
})
export class LoadingComponent implements OnInit, OnDestroy {
  show = false;
  private destroy$ = new Subject<void>();

  constructor(private loaderSvc: LoaderService) { }

  ngOnInit() {
    this.loaderSvc.loaderState
      .pipe(takeUntil(this.destroy$))
      .subscribe((state: LoaderState) => {
        this.show = state.show;
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 07:45:59