Angular 15:如何在Observable订阅前显示表格列加载Spinner?
实现Angular表格列的加载状态显示
要捕获additionalInfo$订阅后的加载时机,核心是利用RxJS的startWith操作符让Observable在返回实际数据前,先发射一个「加载中」的标记值,再结合Angular模板的条件判断实现状态切换。以下是两种实用方案:
方案一:模板+startWith快速实现
不需要修改组件逻辑,直接在Observable创建时添加startWith,配合模板的else分支显示加载状态:
1. 修改Observable创建逻辑
在获取additionalInfo$的方法中,通过startWith先发射null作为加载标记:
// 组件中获取额外信息的方法 getCourseAdditionalInfo(courseId: number): Observable<AdditionalInfo> { return this.apiService.fetchAdditionalInfo(courseId).pipe( startWith(null) // 订阅后立即发射null,标记加载中 ); }
2. 更新模板代码
用async管道结合else模板,判断值为null时显示加载状态:
<tr *ngFor="let course of courseItems$ | async"> <td>{{course.id}}</td> <td>{{course.name}}</td> <td> <ng-container *ngIf="(course.additionalInfo$ | async) as additionalInfo; else loading"> {{additionalInfo.description}} </ng-container> <ng-template #loading> <!-- 替换为你的Spinner组件或加载文本 --> <span class="spinner">Loading ...</span> </ng-template> </td> </tr>
方案二:封装状态对象(支持错误处理)
如果需要同时处理加载、成功、错误三种状态,可以将Observable转换为包含状态的对象,逻辑更清晰:
1. 定义状态接口
interface DataState<T> { loading: boolean; data?: T; error?: string; }
2. 转换Observable为带状态的流
在组件中将courseItems$转换为包含状态的Observable:
// 组件内处理课程列表流 courseItemsWithState$ = this.courseItems$.pipe( switchMap(courses => { const processedCourses = courses.map(course => { const infoState$ = course.additionalInfo$.pipe( map(data => ({ loading: false, data } as DataState<AdditionalInfo>)), startWith({ loading: true } as DataState<AdditionalInfo>), catchError(err => of({ loading: false, error: err.message } as DataState<AdditionalInfo>)) ); return { ...course, infoState$ }; }); return of(processedCourses); }) );
3. 模板中使用状态流
<tr *ngFor="let course of courseItemsWithState$ | async"> <td>{{course.id}}</td> <td>{{course.name}}</td> <td> <ng-container *ngIf="(course.infoState$ | async) as state"> <span *ngIf="state.loading" class="spinner">Loading ...</span> <span *ngIf="state.error" class="error">Error: {{state.error}}</span> <span *ngIf="state.data">{{state.data.description}}</span> </ng-container> </td> </tr>
方案三:自定义startWith管道(无需修改组件)
如果不想修改组件的Observable逻辑,可以自定义一个管道在模板中直接添加startWith:
// 自定义StartWithPipe import { Pipe, PipeTransform } from '@angular/core'; import { Observable, startWith } from 'rxjs'; @Pipe({ name: 'startWith' }) export class StartWithPipe implements PipeTransform { transform<T>(source: Observable<T>, initialValue: T): Observable<T> { return source.pipe(startWith(initialValue)); } }
在模块中声明该管道后,模板可直接使用:
<ng-container *ngIf="(course.additionalInfo$ | startWith: null | async) as info; else loading"> {{info.description}} </ng-container> <ng-template #loading>Loading ...</ng-template>
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

