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

Angular Material Table报错修复及多组件数据缓存方案咨询

问题修复与缓存预取方案

一、先解决 TypeError: Cannot read property 'name' of undefined 错误

这个错误的核心原因有两个,我逐一帮你排查修复:

1. MatTableDataSource 泛型使用错误

你在组件里初始化数据源时写了:

this.dataSource = new MatTableDataSource<Catalog[]>(catalog);

这里的泛型写错了!MatTableDataSource 的泛型参数应该是单个数据项的类型(也就是 Catalog),而不是数组类型。如果你写成 Catalog[],意味着 dataSource.data 是 Catalog[][](数组的数组),那么模板里的 row 就变成了一个数组,自然没有 name 属性,直接导致报错。

修复成:

// 泛型指定单个项类型,或者省略泛型让TS自动推断
this.dataSource = new MatTableDataSource<Catalog>(catalog);
// 或者更简洁:this.dataSource = new MatTableDataSource(catalog);

2. 递归请求中数据拼接的安全处理

你的 getProducts 递归逻辑里,直接展开 p.data,但如果某次接口返回的 p.data 是 undefined 或者 null,展开操作会把 undefined 混入最终数组,导致模板里的 row 变成 undefined,同样触发报错。

修改 service 里的递归拼接逻辑,加上空值兜底:

return this.http.get<any>(url, { headers }).pipe(
  switchMap(p => this.getProducts(p.nextPage, [...dataSoFar, ...(p.data || [])]))
);

这样即使 p.data 为空,也会用空数组替代,避免混入无效值。

额外检查:模板里的点击事件参数

你的模板里点击事件写的是 (click)="openAccountDetails(row.ID)",但你的 Catalog 模型里的属性是小写的 id,这里应该改成 row.id,避免后续出现新的错误。

二、多组件共享数据的预取与缓存方案

因为你的数据变更频率低且需要在多个组件中使用,推荐以下两种方案结合:

1. 单例 Service 缓存数据

利用 Angular Service 的单例特性,在 Service 内部维护缓存,避免重复请求:

修改你的 service,添加缓存逻辑:

import { of, Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

// 新增私有缓存变量
private cachedCatalogs: Catalog[] | null = null;

getData(): Observable<Catalog[]> {
  // 如果有缓存,直接返回缓存数据
  if (this.cachedCatalogs) {
    return of(this.cachedCatalogs);
  }
  // 没有缓存则发起请求,请求完成后存入缓存
  return this.getProducts('/v1/catalog/products').pipe(
    tap(catalogs => this.cachedCatalogs = catalogs)
  );
}

// 可选:提供刷新缓存的方法
refreshCatalogs(): Observable<Catalog[]> {
  this.cachedCatalogs = null; // 清空旧缓存
  return this.getData();
}

这样任何组件调用 getData() 时,第一次会发起请求,后续直接用缓存,无需重复调用接口。

2. 应用初始化时预取数据

如果希望数据在应用启动时就加载完成,后续组件直接使用,可以利用 Angular 的 APP_INITIALIZER:

步骤1:修改 Service,添加初始化方法

在你的 zuora service 里新增:

// 用于APP_INITIALIZER的初始化方法
initialize(): Promise<void> {
  return this.getData().toPromise().then(() => {});
}

步骤2:在 AppModule 中配置 APP_INITIALIZER

import { APP_INITIALIZER } from '@angular/core';
import { ZuoraService } from './path-to-your-service';

// 初始化工厂函数
export function initZuoraCatalog(service: ZuoraService) {
  return () => service.initialize();
}

@NgModule({
  // ...其他配置
  providers: [
    ZuoraService,
    {
      provide: APP_INITIALIZER,
      useFactory: initZuoraCatalog,
      deps: [ZuoraService],
      multi: true
    }
  ]
})
export class AppModule { }

这样应用启动时就会自动加载数据并缓存,所有组件在初始化时都能直接拿到缓存好的数据,无需等待请求。

3. 可选:缓存过期机制

如果数据偶尔需要更新,可以给缓存添加过期时间,比如:

private cachedCatalogs: Catalog[] | null = null;
private cacheExpiry: number | null = null;
private readonly CACHE_TTL = 3600000; // 1小时,单位毫秒

getData(): Observable<Catalog[]> {
  const now = Date.now();
  // 检查缓存是否存在且未过期
  if (this.cachedCatalogs && this.cacheExpiry && now < this.cacheExpiry) {
    return of(this.cachedCatalogs);
  }
  return this.getProducts('/v1/catalog/products').pipe(
    tap(catalogs => {
      this.cachedCatalogs = catalogs;
      this.cacheExpiry = now + this.CACHE_TTL;
    })
  );
}

这样缓存会在1小时后自动失效,下次请求会重新加载数据。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 09:17:28