TypeScript类数据懒加载方案咨询——Angular应用场景
这个按需加载的需求在Angular里实现起来挺顺畅的,刚好我之前做过类似的场景,给你梳理下完整的实现思路:
1. 数据模型结构化设计
首先得明确拆分你的数据类型,Building只保留租户ID数组,Tenant单独定义完整结构:
// models.ts export interface Building { id: number; name: string; address: string; tenantIds: number[]; // 仅存储租户ID,而非完整Tenant对象 } export interface Tenant { id: number; name: string; contact: string; leaseStartDate: Date; // 其他你需要的租户属性,比如租赁面积、到期日期等 }
2. REST API接口配合
你的后端API要对应返回两种数据:
- 获取建筑列表时,返回的
Building对象里只带tenantIds字段 - 提供单独的接口,支持根据ID(或批量ID)获取租户详情
3. Angular服务与组件实现
第一步:创建数据服务
先写一个BuildingService来封装API请求,同时支持批量获取租户,减少HTTP请求次数:
// building.service.ts import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Building, Tenant } from './models'; @Injectable({ providedIn: 'root' }) export class BuildingService { private baseApiUrl = '/api'; // 替换成你的实际API地址 constructor(private http: HttpClient) {} // 获取所有建筑列表(仅带租户ID) getBuildings(): Observable<Building[]> { return this.http.get<Building[]>(`${this.baseApiUrl}/buildings`); } // 批量获取租户详情(推荐用这个,减少请求) getTenantsByIds(tenantIds: number[]): Observable<Tenant[]> { // 这里用URL参数传递ID数组,比如/api/tenants?ids=1,3,5 const idsString = tenantIds.join(','); return this.http.get<Tenant[]>(`${this.baseApiUrl}/tenants?ids=${idsString}`); } // 单个获取租户(可选,当只需要加载单个租户时用) getTenant(tenantId: number): Observable<Tenant> { return this.http.get<Tenant>(`${this.baseApiUrl}/tenants/${tenantId}`); } }
第二步:组件层面实现按需加载
在建筑列表组件里,我们需要:
- 先加载所有建筑列表
- 记录展开的建筑ID,当用户展开时再加载对应租户
- 缓存已加载的租户,避免重复请求
// building-list.component.ts import { Component, OnInit } from '@angular/core'; import { BuildingService } from '../building.service'; import { Building, Tenant } from '../models'; @Component({ selector: 'app-building-list', templateUrl: './building-list.component.html', styleUrls: ['./building-list.component.css'] }) export class BuildingListComponent implements OnInit { buildings: Building[] = []; // 缓存已加载的租户,key是租户ID,value是Tenant对象 private loadedTenants: Record<number, Tenant> = {}; // 记录哪些建筑处于展开状态 expandedBuildingIds = new Set<number>(); constructor(private buildingService: BuildingService) {} ngOnInit(): void { this.loadBuildingList(); } // 加载建筑列表 private loadBuildingList(): void { this.buildingService.getBuildings().subscribe({ next: (buildings) => { this.buildings = buildings; }, error: (err) => { console.error('加载建筑列表失败:', err); // 这里可以加用户友好的错误提示,比如弹出toast } }); } // 切换建筑展开/收起状态,按需加载租户 toggleBuildingExpansion(buildingId: number): void { if (this.expandedBuildingIds.has(buildingId)) { this.expandedBuildingIds.delete(buildingId); return; } this.expandedBuildingIds.add(buildingId); const targetBuilding = this.buildings.find(b => b.id === buildingId); if (!targetBuilding) return; // 过滤出还没加载的租户ID,避免重复请求 const unloadedTenantIds = targetBuilding.tenantIds.filter(id => !this.loadedTenants[id]); if (unloadedTenantIds.length === 0) return; // 批量请求未加载的租户 this.buildingService.getTenantsByIds(unloadedTenantIds).subscribe({ next: (tenants) => { tenants.forEach(tenant => { this.loadedTenants[tenant.id] = tenant; }); }, error: (err) => { console.error('加载租户详情失败:', err); // 同样可以加错误提示 } }); } // 获取当前建筑已加载的租户列表 getLoadedTenantsForBuilding(building: Building): Tenant[] { return building.tenantIds .map(id => this.loadedTenants[id]) .filter(tenant => !!tenant); // 过滤掉还没加载的租户 } }
第三步:组件模板展示
在HTML模板里,我们可以用*ngIf控制展开/收起,同时显示加载状态:
<!-- building-list.component.html --> <div class="building-container"> <div *ngFor="let building of buildings" class="building-card"> <!-- 建筑头部,点击切换展开状态 --> <div class="building-header" (click)="toggleBuildingExpansion(building.id)"> <h2>{{ building.name }}</h2> <p>{{ building.address }}</p> <span class="expand-icon">{{ expandedBuildingIds.has(building.id) ? '▼' : '▶' }}</span> </div> <!-- 展开后的详情区域 --> <div *ngIf="expandedBuildingIds.has(building.id)" class="building-details"> <h3>租户信息</h3> <!-- 显示加载状态 --> <div *ngIf="getLoadedTenantsForBuilding(building).length < building.tenantIds.length" class="loading"> 正在加载租户信息... </div> <!-- 显示已加载的租户 --> <div *ngFor="let tenant of getLoadedTenantsForBuilding(building)" class="tenant-item"> <p><strong>{{ tenant.name }}</strong> - {{ tenant.contact }}</p> <p>租期开始:{{ tenant.leaseStartDate | date:'yyyy-MM-dd' }}</p> </div> <!-- 无租户提示 --> <div *ngIf="building.tenantIds.length === 0" class="no-tenants"> 该建筑暂无租户 </div> </div> </div> </div>
4. 额外优化点
- 缓存持久化:如果需要页面刷新后保留已加载的租户,可以用
localStorage或sessionStorage来缓存loadedTenants - 加载状态细化:可以给每个建筑单独设置加载状态,避免所有展开的建筑都显示同一个加载提示
- 防抖处理:如果用户快速点击展开/收起,可以加防抖,避免不必要的API请求
- 错误重试:可以用RxJS的
retry操作符给API请求加重试逻辑,提升稳定性
内容的提问来源于stack exchange,提问作者Kyle V.
相关产品推荐
相关产品推荐

