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
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
FormGroups
You need choose use a FormArray of FormGroups or an array of FormGroups
I imagine you want to use a FormArray of FormGroups
But, in this case you need iterate over questionFroms.controls and enclosed the loop using formArrayName
NOTE: It’s not "magic". In your code you have an array of FormGroups, so you should use
But, in this case, there’re not relationship between the AddForm and the questionFroms array.