新标签页通过Resolver预取数据时添加进度条的实现方法
解决Angular Resolver加载时显示进度条的问题
你的问题很典型——Resolver在路由激活前获取数据,导致目标组件未渲染时出现空白,而依赖UI的进度条无法触发。这里有两个靠谱的解决方案,帮你在数据解析期间显示进度条:
方案1:利用路由事件全局监听导航状态
这个方案通过监听Angular的路由导航事件,在导航开始时显示进度条,完成/取消/出错时隐藏。优点是无需修改现有Resolver,覆盖所有路由场景:
- 在根组件(比如
AppComponent)中订阅路由事件:
import { Component } from '@angular/core'; import { Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from '@angular/router'; @Component({ selector: 'app-root', template: ` <!-- 进度条放在router-outlet外部,确保始终能渲染 --> <div *ngIf="isLoading" class="fixed-top-progress-bar"> <mat-progress-bar mode="indeterminate"></mat-progress-bar> </div> <router-outlet></router-outlet> ` }) export class AppComponent { isLoading = false; constructor(private router: Router) { this.router.events.subscribe(event => { if (event instanceof NavigationStart) { // 导航开始,立即显示进度条 this.isLoading = true; } else if (event instanceof NavigationEnd || event instanceof NavigationCancel || event instanceof NavigationError) { // 导航结束(无论成功/失败/取消),隐藏进度条 this.isLoading = false; } }); } }
- 给进度条加固定定位的CSS(比如
fixed-top-progress-bar),确保它悬浮在页面顶部,不会被内容遮挡。
方案2:通过全局服务精准控制Resolver的加载状态
这个方案更精准,只在Resolver开始/结束数据获取时控制进度条,不会影响其他路由导航:
步骤1:创建全局Loading服务
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class LoadingService { private isLoadingSubject = new BehaviorSubject<boolean>(false); // 对外暴露可观察对象,避免外部直接修改状态 isLoading$ = this.isLoadingSubject.asObservable(); show(): void { this.isLoadingSubject.next(true); } hide(): void { this.isLoadingSubject.next(false); } }
步骤2:修改Resolver,添加进度条控制
用finalize操作符确保无论请求成功还是失败,进度条都会隐藏:
import { Injectable } from '@angular/core'; import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { DeviceService } from './device.service'; import { LoadingService } from './loading.service'; import { finalize } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class DeviceResolverService implements Resolve<any> { constructor( private deviceService: DeviceService, private loadingService: LoadingService ) {} resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { // 开始获取数据时显示进度条 this.loadingService.show(); return this.deviceService.getDeviceDetails(route.paramMap.get('id')).pipe( // 请求完成(成功/失败)后隐藏进度条 finalize(() => this.loadingService.hide()) ); } }
步骤3:在根组件中使用LoadingService控制进度条
import { Component } from '@angular/core'; import { LoadingService } from './loading.service'; @Component({ selector: 'app-root', template: ` <div *ngIf="isLoading$ | async" class="fixed-top-progress-bar"> <mat-progress-bar mode="indeterminate"></mat-progress-bar> </div> <router-outlet></router-outlet> ` }) export class AppComponent { isLoading$ = this.loadingService.isLoading$; constructor(private loadingService: LoadingService) {} }
注意事项
- 进度条组件必须放在根组件的
router-outlet外部,这样在路由未激活时也能渲染。 - 如果使用Angular Material的进度条,记得导入
MatProgressBarModule到你的模块中。 - 方案2的优势是只针对Resolver的加载场景,不会在普通路由切换时(无Resolver)显示不必要的进度条。
内容的提问来源于stack exchange,提问作者user2900572
相关产品推荐
相关产品推荐

