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

Angular响应式表单报错:Cannot find control with name: 0 求助

Angular表单错误排查:Cannot find control with name: '0' 及路径错误

开发Angular测验题添加组件时,点击添加题目后出现以下错误:
Cannot find control with name: 0
Cannot find control with path: '0 -> questionText'

组件TS代码

@Component({
  selector: 'app-add-quiz',
  templateUrl: './add-quiz.component.html',
  styleUrls: ['./add-quiz.component.css']
})
export class AddQuizComponent implements OnInit {
  public AddForm!: FormGroup;
  public questionForms: FormGroup[] = [];
  public deps: any = [];
  minDate: any;
  maxDate: any;
  private questionIdCounter = 1; 
  currentAnswerId: number = 1;

constructor(private fb: FormBuilder, private router: Router, private auth: AuthService, private api: ApiService) { }

ngOnInit(): void {
}
  addQuestion() {
    this.questionForms.push(this.createQuestionFormGroup());
    console.log(this.questionForms);
    this.questionIdCounter++;
    
  }
  createQuestionFormGroup(): FormGroup {
    return this.fb.group({
      id: this.questionIdCounter,
      questionText: new FormControl('', Validators.required), // Update this line
      selectedAnswerIndex: null,
      answers: this.fb.array([]),
    });
  }

  addAnswer(questionForm: FormGroup) {
    const answersArray = questionForm.get('answers') as FormArray;
    answersArray.push(this.createAnswerFormGroup()); // Set answerText value here
  }

  createAnswerFormGroup() {
    return this.fb.group({
      answerText: new FormControl(['', Validators.required]), // Set default value for answerText
      isCorrect: [false]
    });
  }

  isFormValid(): boolean {
    if (this.AddForm.valid) {
      return true;
    }
    else {
      return false
    }
  }
  async onSubmit() {
    if (this.AddForm.valid) {
      const quizData: QuizRequest = {
        quiz: {
          quizName: this.AddForm.get('quizName')?.value,
          quizStatus: this.AddForm.get('quizStatus')?.value,
          depId: this.AddForm.get('depId')?.value,
          expiryDate: this.AddForm.get('expiryDate')?.value
        },
        questions: this.questionForms.map((questionForm) => {
          const answersArray = questionForm.get('answers') as FormArray;

          return {
            questionText: questionForm.get('questionText')?.value,
            correctAnswerIndex: questionForm.get('selectedAnswerIndex')?.value,
            answers: answersArray.controls.map((answerCtrl) => {
              return {
                answerText: answerCtrl.get('answerText')?.value,
                isCorrect: answerCtrl.get('isCorrect')?.value
              };
            })
          };
        })
      };
 
      console.log('Quiz Data:', quizData);
      try {
        console.log('onSubmit - quizData', quizData);
        await this.api.addQuiz(quizData).toPromise();
        this.showSuccessMessage();
        this.resetForm(); // Reset the form after successful submission
      } catch (error: any) {
        let errorMessage = 'An error occurred while submitting the quiz.';

        if (error.status === 400) {
          errorMessage = 'Bad request: Invalid data provided.';
        } else if (error.status === 403) {
          errorMessage = 'You do not have permission to submit the quiz.';
        } else if (error.status === 500) {
          errorMessage = 'Internal server error. Please try again later.';
        }

        this.showErrorMessage(errorMessage);
      }
    }
  }

HTML代码

<div class="container mt-5">
  <form [formGroup]="AddForm" (ngSubmit)="onSubmit()" class="quiz-form">
   
    <div class="row mt-4">
      <div class="col-md-12">
        <button type="button" class="btn btn-success btn-block" (click)="addQuestion()">Add Question</button>
      </div>
    </div>
    <div>
      <div class="row question-row" *ngFor="let questionForm of questionForms; let i = index" [formGroupName]="i">
        <div class="col-md-12 question-container">
          <div class="form-group question-header">
            <label class="question-label" for="questionText{{ i }}">{{ 'Question ' + (i + 1) }}</label>
            <input formControlName="questionText" type="text" class="form-control" [id]="'questionText_' + i">
            <button type="button" class="btn btn-danger btn-remove" (click)="removeQuestion(i)">Remove Question</button>
          </div>
          <div class="form-group" *ngFor="let answerCtrl of getAnswerControls(questionForm); let j = index">
            <label for="answerText{{ i }}_{{ j }}">{{ 'Answer ' + (j + 1) }}</label>
            <button type="button" class="btn btn-danger " (click)="removeAnswer(questionForm, j)">Remove Answer</button>
            <input formControlName="answerText" type="text" class="form-control" [id]="'answerText_' + i + '_' + j">
          </div>

          <button type="button" class="btn btn-primary btn-add-answer" (click)="addAnswer(questionForm)">Add Answer</button>

        </div>
      </div>
    </div>
    <div class="row submit-row">
      <div class="col-md-12">
        <button type="submit" class="btn btn-primary btn-submit" [disabled]="!isFormValid()">Submit Quiz</button>
      </div>
    </div>
  </form>
</div>

完整错误信息

core.mjs:8400 ERROR Error: Cannot find control with name: '0'
    at _throwError (forms.mjs:3151:11)
    at setUpFormContainer (forms.mjs:3133:9)
    at FormGroupDirective._setUpFormContainer (forms.mjs:4914:9)
    at FormGroupDirective.addFormGroup (forms.mjs:4801:14)
    at FormGroupName.ngOnInit (forms.mjs:3672:28)
    at callHook (core.mjs:2434:22)
    at callHooks (core.mjs:2403:17)
    at executeInitAndCheckHooks (core.mjs:2354:9)
    at selectIndexInternal (core.mjs:9059:17)
    at Module.ɵɵadvance (core.mjs:9042:5)
handleError @ core.mjs:8400
(anonymous) @ core.mjs:25320
invoke @ zone.js:375
run @ zone.js:134
runOutsideAngular @ core.mjs:24109
tick @ core.mjs:25320
(anonymous) @ core.mjs:25169
invoke @ zone.js:375
onInvoke @ core.mjs:24210
invoke @ zone.js:374
run @ zone.js:134
run @ core.mjs:24064
next @ core.mjs:25168
next @ Subscriber.js:91
_next @ Subscriber.js:60
next @ Subscriber.js:31
(anonymous) @ Subject.js:34
errorContext @ errorContext.js:19
next @ Subject.js:27
emit @ core.mjs:20900
checkStable @ core.mjs:24132
onLeave @ core.mjs:24260
onInvokeTask @ core.mjs:24204
invokeTask @ zone.js:408
runTask @ zone.js:178
invokeTask @ zone.js:490
invokeTask @ zone.js:1664
globalCallback @ zone.js:1695
globalZoneAwareCallback @ zone.js:1728
Show 29 more frames
Show less
add-quiz.component.html:70 ERROR Error: Cannot find control with path: '0 -> questionText'
    at _throwError (forms.mjs:3151:11)
    at setUpControl (forms.mjs:2934:13)
    at FormGroupDirective.addControl (forms.mjs:4771:9)
    at FormControlName._setUpControl (forms.mjs:5321:43)
    at FormControlName.ngOnChanges (forms.mjs:5266:18)
    at FormControlName.rememberChangeHistoryAndInvokeOnChangesHook (core.mjs:1521:14)
    at callHook (core.mjs:2444:18)
    at callHooks (core.mjs:2403:17)
    at executeInitAndCheckHooks (core.mjs:2354:9)
    at selectIndexInternal (core.mjs:9059:17)

问题根源

错误本质是表单控件层级不匹配:

  • HTML里用[formGroupName]="i",是告诉Angular当前元素属于AddForm这个FormGroup下的第i个控件,但AddForm初始化时根本没有包含存储题目的FormArray,完全找不到名为0的控件。
  • 同时你把题目存在独立的questionForms数组里,和AddForm没有关联,导致表单指令无法识别控件路径。

修复步骤

1. 初始化AddForm并添加题目FormArray

在ngOnInit里正确初始化表单,包含测验基础信息和题目数组:

ngOnInit(): void {
  this.AddForm = this.fb.group({
    quizName: ['', Validators.required],
    quizStatus: ['', Validators.required],
    depId: ['', Validators.required],
    expiryDate: ['', Validators.required],
    questions: this.fb.array([]) // 新增:存储题目的FormArray
  });
}

// 新增:获取题目FormArray的快捷方法
get questionsArray(): FormArray {
  return this.AddForm.get('questions') as FormArray;
}

2. 修改addQuestion方法,往FormArray里添加题目

不再使用独立的questionForms数组,直接操作AddForm内部的FormArray:

addQuestion() {
  this.questionsArray.push(this.createQuestionFormGroup());
  this.questionIdCounter++;
}

3. 修正HTML中的题目遍历逻辑

遍历questionsArray.controls,并通过formArrayName和formGroupName正确关联表单路径:

<div formArrayName="questions">
  <div class="row question-row" *ngFor="let questionCtrl of questionsArray.controls; let i = index" [formGroupName]="i">
    <div class="col-md-12 question-container">
      <div class="form-group question-header">
        <label class="question-label" for="questionText{{ i }}">{{ 'Question ' + (i + 1) }}</label>
        <input formControlName="questionText" type="text" class="form-control" [id]="'questionText_' + i">
        <button type="button" class="btn btn-danger btn-remove" (click)="removeQuestion(i)">Remove Question</button>
      </div>
      <!-- 答案部分需要嵌套formArrayName -->
      <div formArrayName="answers">
        <div class="form-group" *ngFor="let answerCtrl of getAnswerControls(questionCtrl); let j = index" [formGroupName]="j">
          <label for="answerText{{ i }}_{{ j }}">{{ 'Answer ' + (j + 1) }}</label>
          <button type="button" class="btn btn-danger " (click)="removeAnswer(i, j)">Remove Answer</button>
          <input formControlName="answerText" type="text" class="form-control" [id]="'answerText_' + i + '_' + j">
          <!-- 可选:添加正确答案选择框 -->
          <input type="checkbox" formControlName="isCorrect" id="isCorrect_{{i}}_{{j}}">
          <label for="isCorrect_{{i}}_{{j}}">设为正确答案</label>
        </div>
      </div>

      <button type="button" class="btn btn-primary btn-add-answer" (click)="addAnswer(i)">Add Answer</button>
    </div>
  </div>
</div>

4. 更新答案相关方法,通过索引操作FormArray

原来的addAnswer和removeAnswer需要改为通过题目索引获取对应FormGroup:

addAnswer(questionIndex: number) {
  const questionGroup = this.questionsArray.at(questionIndex) as FormGroup;
  const answersArray = questionGroup.get('answers') as FormArray;
  answersArray.push(this.createAnswerFormGroup());
}

removeAnswer(questionIndex: number, answerIndex: number) {
  const questionGroup = this.questionsArray.at(questionIndex) as FormGroup;
  const answersArray = questionGroup.get('answers') as FormArray;
  answersArray.removeAt(answerIndex);
}

// 更新getAnswerControls方法,接收FormControl而非FormGroup(兼容遍历的questionCtrl)
getAnswerControls(questionCtrl: AbstractControl): AbstractControl[] {
  return (questionCtrl as FormGroup).get('answers')?.controls || [];
}

// 新增删除题目方法
removeQuestion(questionIndex: number) {
  this.questionsArray.removeAt(questionIndex);
}

5. 修正onSubmit和isFormValid逻辑

现在题目数据在AddForm内部,不需要再依赖questionForms数组:

isFormValid(): boolean {
  return this.AddForm.valid;
}

async onSubmit() {
  if (this.AddForm.valid) {
    const formValue = this.AddForm.value;
    const quizData: QuizRequest = {
      quiz: {
        quizName: formValue.quizName,
        quizStatus: formValue.quizStatus,
        depId: formValue.depId,
        expiryDate: formValue.expiryDate
      },
      questions: formValue.questions.map((question: any, index: number) => ({
        questionText: question.questionText,
        correctAnswerIndex: question.answers.findIndex((ans: any) => ans.isCorrect), // 通过isCorrect字段获取正确答案索引
        answers: question.answers.map((answer: any) => ({
          answerText: answer.answerText,
          isCorrect: answer.isCorrect
        }))
      }))
    };
 
    console.log('Quiz Data:', quizData);
    try {
      await this.api.addQuiz(quizData).toPromise();
      this.showSuccessMessage();
      this.AddForm.reset(); // 重置整个表单
      // 重置计数器
      this.questionIdCounter = 1;
    } catch (error: any) {
      let errorMessage = '提交测验时发生错误。';

      if (error.status === 400) {
        errorMessage = '请求错误:提供的数据无效。';
      } else if (error.status === 403) {
        errorMessage = '你没有提交测验的权限。';
      } else if (error.status === 500) {
        errorMessage = '服务器内部错误,请稍后重试。';
      }

      this.showErrorMessage(errorMessage);
    }
  }
}

6. 修正createAnswerFormGroup的小问题

原来的answerText用了数组形式的初始值,改为普通字符串:

createAnswerFormGroup() {
  return this.fb.group({
    answerText: ['', Validators.required], // 去掉外层数组
    isCorrect: [false]
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:27:02