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

Angular15+PrimeNG实现嵌套对象值变更时按表达式联动更新

实现Angular 15 + PrimeNG树形组件的数值联动计算

核心思路

要实现子节点数值变更后向上触发父节点表达式计算,关键是给每个节点添加父节点引用,这样能递归向上溯源;同时借助math.js解析表达式并计算,自动忽略非数值类型的节点。

步骤1:预处理树节点,添加父节点引用

在组件初始化时,递归遍历原始数据,给每个节点新增parent属性,方便后续向上遍历父节点:

import { Component, OnInit } from '@angular/core';
import { evaluate } from 'mathjs';

interface TreeNode {
  id: number;
  data: { value: string | number };
  expression: string | null;
  subElements: TreeNode[];
  parent?: TreeNode; // 新增父节点引用字段
}

@Component({
  selector: 'app-tree-calculator',
  templateUrl: './tree-calculator.component.html'
})
export class TreeCalculatorComponent implements OnInit {
  treeData: TreeNode[] = [];
  // 你的原始嵌套数据
  originalData = {
    id: 1,
    data: { value: "Lorem ipsum" },
    expression: null,
    subElements: [
      {
        id: 2,
        data: { value: 2 },
        expression: "v0 / v1",
        subElements: [
          { id: 4, data: { value: 100 }, expression: null, subElements: [] },
          {
            id: 5,
            data: { value: 50 },
            expression: "v0 + v1",
            subElements: [
              { id: 6, data: { value: 20 }, expression: null, subElements: [] },
              { id: 7, data: { value: 30 }, expression: null, subElements: [] }
            ]
          }
        ]
      },
      { id: 3, data: { value: "Lorem ipsum" }, expression: null, subElements: [] }
    ]
  };

  ngOnInit(): void {
    this.treeData = [this.addParentReferences(this.originalData)];
  }

  private addParentReferences(node: TreeNode, parent?: TreeNode): TreeNode {
    node.parent = parent;
    if (node.subElements.length > 0) {
      node.subElements = node.subElements.map(child => this.addParentReferences(child, node));
    }
    return node;
  }
}

步骤2:编写联动计算核心函数

当输入框数值变化时,先更新当前节点值,再递归向上遍历父节点,依次计算每个有表达式的父节点数值:

calculateExpression(event: any, node: TreeNode): void {
  // 跳过非数值类型的节点
  if (typeof node.data.value !== 'number') return;

  // 从当前节点的父节点开始向上遍历
  let currentParent = node.parent;
  while (currentParent) {
    // 跳过无表达式或非数值类型的父节点
    if (typeof currentParent.data.value !== 'number' || !currentParent.expression) {
      currentParent = currentParent.parent;
      continue;
    }

    // 收集子节点数值:v0对应第一个子节点,v1对应第二个,以此类推
    const variables: Record<string, number> = {};
    currentParent.subElements.forEach((child, index) => {
      if (typeof child.data.value === 'number') {
        variables[`v${index}`] = child.data.value as number;
      }
    });

    try {
      // 用math.js计算表达式,保留两位小数
      const result = evaluate(currentParent.expression, variables);
      currentParent.data.value = Number(result.toFixed(2));
    } catch (error) {
      console.error('表达式计算失败:', error);
    }

    // 继续向上处理父节点的父节点
    currentParent = currentParent.parent;
  }
}

步骤3:修改模板,控制输入框显示

在p-tree模板中,仅对数值类型的节点渲染p-inputNumber,非数值节点直接显示文本:

<p-tree [value]="treeData">
  <ng-template pTemplate="default" let-node>
    <div class="flex align-items-center">
      <!-- 仅数值类型节点显示输入框 -->
      <ng-container *ngIf="typeof node.data.value === 'number'">
        <p-inputNumber
          [(ngModel)]="node.data.value"
          (onInput)="calculateExpression($event, node)"
          [minFractionDigits]="2"
          [maxFractionDigits]="2"
        ></p-inputNumber>
      </ng-container>
      <!-- 非数值类型节点显示文本 -->
      <span *ngIf="typeof node.data.value !== 'number'">{{ node.data.value }}</span>
    </div>
  </ng-template>
</p-tree>

前置准备

确保已安装math.js,执行命令:

npm install mathjs

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 17:02:10