Angular响应式表单提交报错:无法找到未指定名称属性的控件
解决Angular响应式表单动态复选框的"Cannot find control with unspecified name attribute"错误
问题情况
开发Angular讲师分配课程组件,采用响应式表单实现,基于API返回数据动态生成课程选择复选框。提交表单能成功获取值,但控制台抛出以下错误:
assign-course-to-instructor.component.ts:56 ERROR Error: Cannot find control with unspecified name attribute at _throwError (forms.mjs:3150:11) at setUpControl (forms.mjs:2933:13) at FormControlDirective.ngOnChanges (forms.mjs:4570:13) at FormControlDirective.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:3032:14) at callHookInternal (core.mjs:4024:14) at callHook (core.mjs:4055:9) at callHooks (core.mjs:4006:17) at executeInitAndCheckHooks (core.mjs:3956:9) at selectIndexInternal (core.mjs:11780:17) at Module.ɵɵadvance (core.mjs:11763:5)
TypeScript代码
import { AfterViewInit, Component, OnInit } from '@angular/core'; import { FormArray, FormControl, FormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { concatMap } from 'rxjs/operators'; import { CourseResponse } from 'src/app/models/course-response'; import { DepartmentResponse } from 'src/app/models/department-response'; import { InstructorResponse } from 'src/app/models/instructor-response'; import { DataService } from 'src/app/services/data.service'; @Component({ selector: 'app-assign-course-to-instructor', templateUrl: './assign-course-to-instructor.component.html', styleUrls: ['./assign-course-to-instructor.component.css'] }) export class AssignCourseToInstructorComponent implements OnInit { instructor_resp: InstructorResponse; deptList: DepartmentResponse[] = []; courseList: CourseResponse[] = []; courseForm: FormGroup = new FormGroup({ courses: new FormArray([]) }); constructor(private dataService: DataService, private route: ActivatedRoute) { this.instructor_resp = { id: -1, name: '', department: { id: -1, name: '' }, dob: new Date(), gender: '', userAuth: { id: -1, email: '', password: '', role: '' }, courses: [] }; this.courseForm.setControl('courses', new FormArray([])); } ngOnInit(): void { this.dataService.isLoddedIn().pipe( concatMap(() => this.dataService.getCourseList()), concatMap((res) => { this.courseList = res; return this.dataService.getDepartmentList(); }), concatMap((res) => { this.deptList = res; return this.dataService.getInstructorById(this.route.snapshot.params['id']); }) ).subscribe({ next: (res) => { this.instructor_resp = res; this.instructor_resp.dob = new Date(res.dob); }, error: (err) => { console.log(err); },complete:()=>{ this.updateCoursesFormArray(); } }); } private updateCoursesFormArray() { const coursesFormArray = this.courseForm.get('courses') as FormArray; this.courseList.forEach(() => { coursesFormArray.push(new FormControl(false)); }); } getCoursesControl(index: number): FormControl { const coursesFormArray = this.courseForm.get('courses') as FormArray; return coursesFormArray.at(index) as FormControl; } onSubmit(): void { console.log(this.courseForm.value) } }
HTML代码
<div class="container"> <div class="instructor-details"> <ul> <li>Id: {{ instructor_resp.id }}</li> <li>Name: {{ instructor_resp.name }}</li> <li>Email: {{ instructor_resp.userAuth.email }}</li> <li>DOB: {{ instructor_resp.dob | date: 'dd-MM-yyyy' }}</li> <li>Department: {{ instructor_resp.department.name }}</li> <li>Gender: {{ instructor_resp.gender }}</li> </ul> <div> <h5>Filter Course using </h5> <select> <option [selected]="true" [hidden]="true" [disabled]="true">Select Department</option> <option *ngFor="let dept of deptList" [value]="dept.id">{{ dept.name }}</option> </select> <select> <option [selected]="true" [hidden]="true" [disabled]="true">Select Semester</option> <option value="SEM_1">Sem-1</option> <option value="SEM_2">Sem-2</option> <option value="SEM_3">Sem-3</option> <option value="SEM_4">Sem-4</option> <option value="SEM_5">Sem-5</option> <option value="SEM_6">Sem-6</option> <option value="SEM_7">Sem-7</option> <option value="SEM_8">Sem-8</option> <option value="SEM_9">Sem-9</option> <option value="SEM_10">Sem-10</option> </select> </div> </div> <div class="course-container"> <form [formGroup]="courseForm" (submit)="onSubmit()"> <div> <h3>Choose Courses from below</h3> <div class="item" > <label>Select Courses</label> <div formArrayName="courses" *ngFor="let course of courseList; let i = index" class="checkbox-item"> <input type="checkbox" [formControl]="getCoursesControl(i)"> {{ course.courseName }} </div> </div> </div> <button type="submit">Submit</button> </form> </div> </div>
解决思路与修改方案
1. 移除冗余的FormArray初始化
构造函数中已在courseForm初始化时创建了courses FormArray,后续重复调用this.courseForm.setControl('courses', new FormArray([]))属于多余操作,直接删除该行代码。
2. 修正FormArray的模板绑定方式
错误核心原因是formArrayName与*ngFor放在同一元素,且控件绑定逻辑不符合Angular表单规范,修改如下:
步骤1:添加FormArray getter简化访问
在TS类中添加getter,方便模板直接获取FormArray:
get courses(): FormArray { return this.courseForm.get('courses') as FormArray; }
步骤2:调整HTML模板结构
将formArrayName放在父容器,*ngFor移至内部元素,用[formControlName]绑定索引:
<div class="item"> <label>Select Courses</label> <!-- 父容器设置formArrayName --> <div formArrayName="courses" class="checkbox-container"> <!-- 内部循环遍历课程列表和FormArray索引 --> <div *ngFor="let course of courseList; let i = index" class="checkbox-item"> <input type="checkbox" [formControlName]="i"> {{ course.courseName }} </div> </div> </div>
3. 优化:预勾选讲师已分配课程
当前updateCoursesFormArray中所有控件默认设为false,可根据讲师已有课程设置初始值:
private updateCoursesFormArray() { const coursesFormArray = this.courseForm.get('courses') as FormArray; this.courseList.forEach(course => { // 检查课程是否已分配给讲师 const isAssigned = this.instructor_resp.courses.some(c => c.id === course.id); coursesFormArray.push(new FormControl(isAssigned)); }); }
4. 可选:添加模板守卫避免空值渲染
为防止API数据未加载完成时模板报错,给表单容器添加*ngIf:
<form [formGroup]="courseForm" (submit)="onSubmit()" *ngIf="courseForm && courses.length > 0"> <!-- 表单内容 --> </form>
验证效果
完成修改后,控制台错误会消失,表单提交仍能正常获取选中值,同时讲师已有的课程会自动勾选。
内容的提问来源于stack exchange,提问作者Md Asad Ali
相关产品推荐
相关产品推荐

