Angular 13中FormGroup添加校验导致下拉框空白如何解决
下拉框空白不是本身数据绑定逻辑的问题,核心原因是嵌套表单组的控件访问路径写错,导致模板渲染抛错中断。
你定义的get f()只返回最外层表单的一级控件,仅包含name、currency、address三个属性。但你在formGroupName="address"的嵌套块中直接用f['street']、f['city']这类写法访问控件,相当于在最外层表单里找不存在的street、city属性,Angular执行到这部分代码时会抛出Cannot read properties of undefined (reading 'touched')的JS异常,直接中断后续渲染流程——本来要通过*ngFor循环生成的货币下拉选项没来得及渲染,就表现为下拉框空白。
另外你的代码还存在两个隐藏问题:
- 表单的address组中没有定义
zipCode控件,但模板里写了zipCode的输入框和校验逻辑,就算路径正确也会触发找不到控件的错误 - select下拉的option标签没有绑定值属性,选中选项后无法正确同步值到表单控件
1. 修正嵌套表单控件的访问路径
两种实现方式选其一即可:
方式一:新增嵌套表单组的getter(推荐,代码更简洁)
在组件类中补充address表单组的获取方法:
get f() { return this.form.controls; } // 新增嵌套address组的getter get addressForm() { return this.form.controls.address as FormGroup; }
将模板中address下所有控件的校验访问逻辑,从原来的f['street']替换为addressForm.controls['street'],以street控件为例:
<div *ngIf="addressForm.controls['street'].touched && addressForm.controls['street'].invalid" class="alert alert-danger"> Street is required. <div *ngIf="addressForm.controls['street'].errors?.['required']">Street is required.</div> </div>
city、zipCode、country控件的校验逻辑按相同规则替换即可。
方式二:模板内直接写全访问路径
如果不想新增getter,也可以直接在模板中写全嵌套路径,不需要修改组件类代码:
<div *ngIf="f['address'].controls['street'].touched && f['address'].controls['street'].invalid" class="alert alert-danger"> Street is required. <div *ngIf="f['address'].controls['street'].errors?.['required']">Street is required.</div> </div>
2. 补全缺失的表单控件定义
你当前的address表单组只定义了street、city、country三个控件,和模板里的zipCode输入框不匹配,要么在表单定义中补全zipCode,要么删掉模板中多余的zipCode相关代码。补全控件的修改如下:
form = new FormGroup({ name: new FormControl('',[Validators.required]), currency: new FormControl('', Validators.required), address: new FormGroup({ street: new FormControl('',[Validators.required]), city: new FormControl('',[Validators.required]), zipCode: new FormControl('',[Validators.required]), // 补全缺失的zipCode控件 country: new FormControl('',[Validators.required]), }) });
3. 修复下拉框选项的值绑定
当前option标签没有绑定值属性,选中后无法正确同步数据到表单,给循环生成的选项添加[ngValue]绑定,同时删掉多余的无效空选项:
<select formControlName="currency" class="form-control"> <option value="" disabled>Select Currency</option> <option *ngFor="let currency of currencies" [ngValue]="currency">{{currency.name}}</option> </select>
如果需要绑定货币ID、名称等单个字段而非整个对象,把[ngValue]的内容替换为对应字段即可,例如[ngValue]="currency.id"。
修改完成后打开浏览器开发者控制台,之前的undefined访问错误会消失,货币接口返回的数据会正常渲染在下拉选项中,各输入框的校验提示可以正常触发,不会再中断页面渲染,表单提交时也能拿到完整正确的表单值。
内容的提问来源于stack exchange,提问作者Toni0123

