I have a list of questions loaded from WebAPI. Based on the questionType, the answer is either radio options/text. Now I want to validate my answers(required/custom validator). I added formcontrol to formgroup dynamically after the webAPI returns the list of questions.
Error - NG0100: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: '[object Object]'. Current value: 'undefined'..
If *ngIf="questionForm" is removed from html, then Error - formGroup expects a FormGroup instance. Please pass one in.
To my understanding, the formgroup instance is created and it gets reloaded as undefined again somewhere in the angular lifecycle.
.html
<form *ngIf="questionForm" [formGroup]="questionForm">
<ng-container *ngFor="let item of Questions;let i = index;">
<mat-label >{{item.question}}</mat-label>
<div [ngSwitch]="item.questionType">
<div *ngSwitchCase="'bool'">
<mat-radio-group aria-
label="Select an option">
<mat-radio-button id="YesAnswer_{{item.id}}"
#Answer_{{item.id}}
name="Answer_{{item.id}}" value="1"
formControlName="Answer_{{item.id}}">Yes</mat-radio-
button>
<mat-radio-button id="NoAnswer_{{item.id}}"
#Answer_{{item.id}}
name="Answer_{{item.id}}" value="0"
formControlName="Answer_{{item.id}}">No</mat-radio-
button>
</mat-radio-group>
</div>
<div *ngSwitchCase="'string'">
<mat-form-field appearance="outline">
<input matInput type="text"
id="Answer_{{item.id}}" #Answer_{{item.id}}
formControlName="Answer_{{item.id}}"
name="Answer_{{item.id}}">
</mat-form-field>
</div>
</div>
</ng-container>
</form>
.ts file ngOnInit() method. this.Questions holds the list of questions from WebAPI and I made sure that the below code runs only after the Questions are loaded.Adding a console.log(this.questionForm); below this statement shows the list of controls in the FormGroup.
ngOnInit():void{
this.service.GetQuestion().subscribe({
error: (err) => { console.log(err); },
next: (data) => {
if (data) {
this.Questions = data as Questions[];
this.questionForm = new FormGroup ({});
this.Questions.forEach(quest=>{
this.questionForm.addControl('Answer_' String(quest.id),new FormControl( '',
[Validators.required]));
});
}
CodePudding user response:
Try to rename the name of the Interface Questions
to Question
Well, I want to say your that when you use a mat-radio-group, it's this where you use formControlName. BTW I don't like so much the use of interpolation, use binding and you has wrong positioned the label.
This is my .html
<form *ngIf="questionForm" [formGroup]="questionForm">
<ng-container *ngFor="let item of Questions; let i = index">
<div [ngSwitch]="item.questionType">
<div *ngSwitchCase="'bool'">
<mat-label>{{ item.question }}</mat-label>
<div>
<mat-radio-group
[formControlName]="'Answer_' item.id"
aria-label="Select an option"
>
<mat-radio-button value="1">Yes</mat-radio-button>
<mat-radio-button value="0">No</mat-radio-button>
</mat-radio-group>
</div>
</div>
<div *ngSwitchCase="'string'">
<mat-form-field appearance="outline">
<mat-label>{{ item.question }}</mat-label>
<input matInput type="text" [formControlName]="'Answer_' item.id" />
</mat-form-field>
</div>
</div>
</ng-container>
</form>
And a stackblitz that works (and is your code -well, I change the interface Questions by Question)
BTW, when you use a FormArray of FormControls you can use validator only in the FormControls you need -not in the whole FormArray-
CodePudding user response:
Fixed the issue...Its not the issue with creating dynamic form group, its where I declared the formgroup...
I had a child component in this parent component and used @ViewChild decorator for the child component reference. When I declared my formgroup, I placed inbetween these two statement, which I overlooked and that caused all the errors.
@ViewChild('myChild')
public myChildControl: MyChildComponent;
By mistakenly placed my formgroup declaration like below
@ViewChild('myChild')
questionForm: FormGroup =new FormGroup({});
public myChildControl: MyChildComponent;
Moving it below my childcomponent reference fixed the errors.
Thanks Eliseo for your input.