Angular中如何使用FormBuilder验证带动态值的表单数组
Angular 动态表单(FormArray)字段校验及报错修复方案
你遇到的Cannot read properties of null (reading 'invalid')报错主要有2个核心原因:
- 模板中调用
data.get()时参数没有加引号,你写的data.get(subject)会被解析为读取组件内的subject变量,而不是获取名为subject的表单控件,找不到变量返回null,调用invalid就会抛出报错。 - 初始化
PersonalDataFormArray时,错误使用了formBuilder.control,而每个子项实际是FormGroup类型,类型不匹配也会导致控件读取异常。
第一步:修复TypeScript逻辑
调整表单初始化、动态行创建逻辑,给每个动态字段加上必填校验规则:
// 全局变量控制提交状态,和Name字段的校验逻辑对齐 submitted = false; constructor(private formBuilder: FormBuilder) {} ngOnInit() { this.SettingsForm = this.formBuilder.group({ id: [''], Name: ['', Validators.required], // 原control改为group,默认初始化第一行表单 PersonalData: this.formBuilder.array([this.newPersonalData()], Validators.required) }); } PersonalData() : FormArray { return this.SettingsForm.get("PersonalData") as FormArray } newPersonalData(): FormGroup { return this.formBuilder.group({ id: '', // 每个字段添加必填校验 subject: ['', Validators.required], mark: ['', Validators.required], // 多选框默认值为数组格式,添加必填校验 exams: [[], Validators.required], active: [true] // 模板中用到了active字段,提前定义避免取值报错 }) } addPersonalData() { this.PersonalData().push(this.newPersonalData()); } deletedata(index: number) { // 最少保留1行,只有1行时标记为隐藏,有多行直接移除 if(this.PersonalData().length > 1) { this.PersonalData().removeAt(index); } else { this.PersonalData().at(index).get('active')?.setValue(false); } } // 提交方法 SettingsFormSubmit() { this.submitted = true; // 表单整体校验不通过则终止提交 if(this.SettingsForm.invalid) { // 遍历所有动态行,手动触发所有控件校验状态更新,确保所有错误提示都正常展示 this.PersonalData().controls.forEach(group => { group.markAllAsTouched(); }); return; } // 校验通过后执行你的提交逻辑 console.log(this.SettingsForm.value); }
第二步:修复HTML模板
给data.get()的参数加上引号,同时和提交状态submitted、控件touched状态联动,避免页面刚加载就展示错误提示:
<form [formGroup]="SettingsForm" (ngSubmit)="SettingsFormSubmit()"> <div class="row"> <div class="col-xl-4"> <input type="text" placeholder="Name" formControlName="Name"> </div> </div> <div class="row"> <div class="col-xl-4 "> <p *ngIf="submitted && SettingsForm.get('Name')?.invalid" class="text-danger"> Name is required. </p> </div> </div> <div class="row"> <div class="col-xl-12"> <table class="table" formArrayName="PersonalData"> <thead> <tr> <th>Subject</th> <th>Mark</th> <th>Exams</th> <th>Delete</th> <th><button type="button" (click)="addPersonalData()" class="btn-blank">Add</button></th> </tr> </thead> <tbody> <tr [hidden]="!data.value.active" *ngFor="let data of PersonalData().controls;let i=index" [formGroupName]="i"> <td> <select class="form-control" formControlName="subject"> <option value="">Select Subject</option> <ng-container *ngFor="let subject of subjects"> <option [value]="subject.id">{{ subject.subject_name }}</option> </ng-container> </select> <!-- 字段名加引号,加安全操作符?.避免空指针 --> <ng-container *ngIf="submitted && data.get('subject')?.invalid && data.get('subject')?.touched"> <span class="text-danger">请选择科目</span> </ng-container> </td> <td> <select class="form-control" formControlName="mark"> <option value="">Select Mark</option> <option>50</option> <option>60</option> <option>90</option> </select> <ng-container *ngIf="submitted && data.get('mark')?.invalid && data.get('mark')?.touched"> <span class="text-danger">请选择分数</span> </ng-container> </td> <td> <mat-form-field> <mat-label>Select Exams</mat-label> <mat-select multiple formControlName="exams"> <mat-option *ngFor="let term of terms" [value]="term.id"> {{term.name}} </mat-option> </mat-select> </mat-form-field> <ng-container *ngIf="submitted && data.get('exams')?.invalid && data.get('exams')?.touched"> <span class="text-danger">请至少选择一项考试</span> </ng-container> </td> <td><button type="button" (click)="deletedata(i)" >Delete</button></td> <td><button type="button" (click)="addPersonalData()" >Add</button></td> </tr> </tbody> </table> </div> </div> <div class="modal-footer"> <button type="submit" class="btn btn-success">Submit</button> </div> </form>
优化说明
- 所有
select的默认选项都添加了value="",和表单控件的初始空值对应,避免默认选中异常 - 新增了安全操作符
?.,避免控件未初始化时读取属性抛出空指针报错 - 提交时调用
markAllAsTouched(),确保所有未填写的字段都会触发错误提示 - 多选框
exams初始值设为[]空数组,符合Angular多选组件的赋值要求,必填校验可以正常识别未选中的状态
内容的提问来源于stack exchange,提问作者mevr
相关产品推荐
相关产品推荐

