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

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.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:02:44