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

Angular中如何展示动态ViewModel并通过字符串名获取模型属性值

Angular动态报表组件实现方案(支持任意ViewModel)

核心问题的解决本质是利用JS/TS原生的动态属性访问语法,不需要引入第三方库就能实现。

核心:字符串属性名动态取值实现

  • 单层属性直接用方括号语法访问,这是JS原生支持的特性:
// 任意结构的ViewModel实例
const userVM = { name: "李四", age: 30, post: "前端工程师" };
// 字符串类型的属性名
const key = "name";
// 动态取值结果为"李四"
const value = userVM[key];
  • 如果是嵌套结构的属性(比如department.leader.name这种多层路径),写一个轻量的路径解析工具函数即可,不需要引入额外依赖:
/**
 * 根据点分隔的属性路径获取对象嵌套值
 * @param target 目标ViewModel实例
 * @param propPath 属性路径字符串,比如 'user.contact.email'
 * @returns 对应属性值,路径不存在时返回undefined
 */
function getValueByPath(target: Record<string, any>, propPath: string): any {
  return propPath.split('.').reduce((current, key) => {
    return current ? current[key] : undefined;
  }, target);
}

通用动态报表组件完整实现

1. 组件核心逻辑

组件只需要约定两个输入参数,不需要和具体ViewModel耦合:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-dynamic-report',
  styles: [`
    .report-table { width: 100%; border-collapse: collapse; }
    .report-table th, .report-table td { border: 1px solid #eee; padding: 8px; }
  `],
  template: `
    <table class="report-table">
      <thead>
        <tr>
          <th *ngFor="let col of columnConfigs" [style.text-align]="col.align || 'left'">
            {{ col.label }}
          </th>
        </tr>
      </thead>
      <tbody>
        <tr *ngFor="let row of dataList">
          <td *ngFor="let col of columnConfigs" [style.text-align]="col.align || 'left'">
            <!-- 动态取值渲染,支持传入自定义格式化函数 -->
            {{ col.formatter ? col.formatter(getValueByPath(row, col.prop)) : getValueByPath(row, col.prop) }}
          </td>
        </tr>
      </tbody>
    </table>
  `
})
export class DynamicReportComponent {
  /**
   * 报表数据源,支持任意结构的ViewModel数组
   */
  @Input() dataList: Record<string, any>[] = [];
  /**
   * 列配置数组
   * 配置项说明:
   * - label: 列表头显示文本
   * - prop: 对应绑定的属性名/属性路径(字符串类型)
   * - align: 可选,列对齐方式
   * - formatter: 可选,值格式化函数
   */
  @Input() columnConfigs: Array<{
    label: string;
    prop: string;
    align?: 'left' | 'center' | 'right';
    formatter?: (value: any) => string | number;
  }> = [];

  getValueByPath(target: Record<string, any>, propPath: string): any {
    return propPath.split('.').reduce((current, key) => {
      return current ? current[key] : undefined;
    }, target);
  }
}

2. 业务侧使用方式

不管是用户ViewModel、订单ViewModel还是其他任意业务模型,只需要传入对应的数据和列配置即可,不需要修改动态报表组件的代码:

// 业务组件中示例:用订单ViewModel渲染报表
interface OrderVM {
  orderNo: string;
  amount: number;
  buyer: {
    name: string;
    phone: string;
  };
  payTime: string;
}

// 列配置,prop字段直接写对应属性的字符串名/路径
orderColumns = [
  {label: '订单号', prop: 'orderNo', align: 'left'},
  {label: '订单金额', prop: 'amount', align: 'right', formatter: (val) => `¥${val.toFixed(2)}`},
  {label: '买家姓名', prop: 'buyer.name', align: 'left'},
  {label: '买家电话', prop: 'buyer.phone', align: 'left'},
  {label: '支付时间', prop: 'payTime', align: 'left'}
];

// 接口返回的实际业务数据
orderList: OrderVM[] = [];

业务组件模板中直接调用:

<app-dynamic-report
  [dataList]="orderList"
  [columnConfigs]="orderColumns"
></app-dynamic-report>

3. 扩展建议

  • 如果需要支持自定义单元格渲染(比如按钮、标签、进度条等特殊组件),可以结合ngTemplateOutlet实现动态模板注入,在列配置中支持传入自定义模板引用即可。
  • 不要为了方便直接给组件输入设置any类型,用Record<string, any>做通用类型约束,能避免很多低级的拼写错误。
  • 如果属性路径存在数组场景(比如orderItems[0].productName),只需要扩展getValueByPath函数,增加数组下标解析逻辑就行,核心思路不变。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:42:15