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

Angular获取步进器动态表单对象数组值及求和报错排查

Clarity步进器动态表单取值求和问题修复

问题概述

需要获取动态生成的步进器表单输入值,执行百分比求和校验,现存两个问题:

  • 除最后一个步骤外,其余步骤的表单值在切换步骤后丢失,checkPercentage方法内无法正常读取表单数据
  • 求和逻辑中reduce方法报类型错误:Operator '+' cannot be applied to types 'unknown' and 'unknown',无法完成计算

原始问题代码

表单模板代码

<form
  clrStepper
  clrForm
  #allocationForm="ngForm"
  (ngSubmit)="submit(allocationForm.value)"
>
  <clr-stepper-panel ngModelGroup="{{allocation.type}}" *ngFor="let allocation of allocations; let last = last" #{{allocation.type}}="ngModelGroup">
    <clr-step-title>{{allocation.type}}</clr-step-title>
    <clr-step-content *clrIfExpanded>
      <p style="margin-top: 24px">Der Gesamtbetrag muss 100% betragen.</p>
      <div class="clr-row">
        <div class="clr-col-7" style="margin-top: 24px;">
          <clr-input-container style="display: flex; flex-direction: row; min-height: 48px; margin-top: 8px;" *ngFor="let sub of allocation.subtype; let i = index">
            <label class="clr-col-4">{{sub.name}}</label>
            <input class="clr-col-3" name="{{'value'+ i}}" clrInput [min]="0" [max]="100"
                   clrNumeric pattern="^[0-9][0-9]?$|^100$" required [(ngModel)]="sub.value" clrUnit="%" value="{{sub.value}}">
            <clr-control-error class="text-center" *clrIfError>0-100%</clr-control-error>
          </clr-input-container>
        </div>
      </div>
      <button clrStepButton="next" (click)="checkPercentage(allocationForm)" *ngIf="!last">WEITER</button>
      <button clrStepButton="submit" *ngIf="last">SPEICHERN UND BEENDEN</button>
    </clr-step-content>
  </clr-stepper-panel>
</form>

组件逻辑代码

allocationForm: NgForm;
allocations = [
  {
    type: 'Department',
    subtype: [
      { name: 'AR', value: 0 },
      { name: 'SC ', value: 0 },
      { name: 'SCH ', value: 0 },
      { name: 'LAC ', value: 0 }
    ],
  },
  {
    type: 'Groups',
    subtype: [
      { name: 'LAC', value: 0 },
      { name: 'ME ', value: 0 },
      { name: 'RR ', value: 0 },
      { name: 'SP ', value: 0 }
    ],
  }
]

submit(allocationForm: {}) {
  console.log('form submit', Object.values(allocationForm));
  for (let value of Object.values(allocationForm)) {
    console.log(value)
  }
}

checkPercentage(allocationForm: NgForm) {
  // 求和逻辑此处报错
  // const sum = Object.values(allocationForm).reduce((acc, cur) => acc + cur, 0);
}

问题原因与修复

1. 步骤切换后表单值丢失

核心原因是Clarity Stepper的*clrIfExpanded指令默认开启destroyOnHide配置,步骤切换时会销毁非激活步骤的DOM节点,对应注册的表单控件也会从表单组中移除,值自然无法留存。另外模板中存在两处无效/冲突写法:

  • Angular不支持动态插值声明模板引用变量,#{{allocation.type}}="ngModelGroup"属于非法语法,直接删除即可
  • 输入框同时使用[(ngModel)]双向绑定和value属性会产生冲突,删除多余的value="{{sub.value}}"属性

修复方法:
给*clrIfExpanded传入配置关闭销毁逻辑,修改对应行代码:

<clr-step-content *clrIfExpanded="{destroyOnHide: false}">

修改后切换步骤时已填内容的DOM不会被销毁,表单控件会持续存在,值不会丢失。


2. reduce求和类型报错

报错原因有两个:

  • 直接遍历allocationForm.value时,返回值类型TS会推断为unknown,因为表单结构是动态生成的,TS无法确认每个属性的类型
  • allocationForm.value的最外层是各个步骤的ngModelGroup对象,不是纯数字值,直接累加会把对象转成字符串拼接,逻辑本身也有问题

修复方法:
输入值已经通过双向绑定到了allocations数组的sub.value字段,不需要从表单对象中二次取值,直接遍历当前步骤的subtype数组求和即可,完全不会有类型问题。首先修改模板上的按钮点击事件,传入当前步骤对应的allocation数据:

<button clrStepButton="next" (click)="checkPercentage(allocation)" *ngIf="!last">WEITER</button>

再修改TS中的checkPercentage方法:

checkPercentage(currentAllocation: {type: string, subtype: {name: string, value: number}[]}) {
  // 直接从数据源求和,逻辑简单无类型问题
  const sum = currentAllocation.subtype.reduce((acc, cur) => acc + Number(cur.value), 0);
  console.log('当前步骤百分比总和:', sum);
  if (sum !== 100) {
    // 总和不等于100时的拦截逻辑,比如阻止步骤跳转、提示错误
    alert('总占比必须为100%');
    return;
  }
}

如果一定要从表单对象取值,需要加类型断言明确值的类型:

checkPercentage(allocationForm: NgForm, currentType: string) {
  const currentGroupValue = allocationForm.controls[currentType].value as Record<string, number>;
  const sum = Object.values(currentGroupValue).reduce((acc, cur) => acc + Number(cur), 0);
  console.log(sum);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:42:25