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
相关产品推荐
相关产品推荐

