skip to Main Content

I have this component to add question and answers for each question and to select the correct answer.
I found this problem in many questions in StackOverflow but no luck. Please help me for figuring out what I am doing wrong.

@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);
      }
    }
  }

and for the 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>

and when i try to add a new question i get these errors

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)

2

Answers


  1.  <div *ngIf="questionForms.length">
      <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>
    

    Add ngIf before rendering form controls, since initially your form is just an empty array. Thats why its not possible to find a control with name ‘0’

    But i would say its much better to refactor your solution to formArray

    Login or Signup to reply.
    1. Your questionForms is an array of FormGroups, not a FormArray of
      FormGroups
    2. Your AddForm is "nothing" and is not related with questionForms

    You need choose use a FormArray of FormGroups or an array of FormGroups

    I imagine you want to use a FormArray of FormGroups

      public AddForm!: FormGroup=this.fb.group({
          questions:this.fb.array([])
      })
    
      public get questionForms(){
         return this.AddForm.get('questions') as FormArray
      }
    

    But, in this case you need iterate over questionFroms.controls and enclosed the loop using formArrayName

    <form [formGroup]="AddForm">
      <!--see you say the formArrayName-->
      <div formArrayName="questions">
         <!--see you iterate over questionForms-->
         <div *ngFor="let group of questionForms.controls;let i=index"
                      [formGroupName]="i">
            ...
         </div>
      </div>
    </form>
    

    NOTE: It’s not "magic". In your code you have an array of FormGroups, so you should use

         <!--see that you iterate over an array-->
         <!--you use [formGroup]-->
         <div *ngFor="let group of questionForms;let i=index"
                      [formGroup]="group">
            ...
         </div>
    

    But, in this case, there’re not relationship between the AddForm and the questionFroms array.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search