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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 20:52:53