I have a form which is having nested objects array. For representing multi level form view model we are using ngModelGroup with which we can map parent child relation. Is there a way to represent array using a directive something similar to ngModelGroup?
below are the models,
export class SurveyViewModel {
Survey: Survey;
QuestionAnswerSets: QuestionAnswerSet[];
}
export class Survey {
Id: string;
Name: string;
}
export class QuestionAnswerSet {
Question: Question;
Answers: Answer[];
}
below is the view,
<form [formGroup]="surveyForm" (ngSubmit)="onFormSubmit(surveyForm)">
<div formGroupName="Survey">
<input type="hidden" name="Id" formControlName="Id">
<label>{{model.Survey.Name}}</label>
</div>
<div class='qa-set' formArrayName="QuestionAnswerSets">
<fieldset *ngFor="let questionAnswerSet of surveyForm.controls.QuestionAnswerSets.controls; index as setId;" [formGroupName]="setId">
<div formGroupName="Question">
<input type="hidden" name="Id" formControlName="Id">
<label>{{setId+1}}. </label>
<label>{{model.QuestionAnswerSets[setId].Question.Value}}</label>
</div>
<div class="row" formArrayName="Answers">
<div *ngFor="let answer of this.surveyForm.controls.QuestionAnswerSets.controls[setId].controls.Answers.controls; index as answerId;"
[formGroupName]="answerId">
<div class="col-12 col-sm-3" formGroupName="Answer">
<input type="hidden" name="Id" formControlName="Id">
<label>
<input type="radio" name="Value" formControlName="Value" /> {{model.QuestionAnswerSets[setId].Answers[answerId].Value}}
</label>
</div>
</div>
</div>
</fieldset>
</div>
<div class="row form-group">
<div class="col-12">
<button type="button" class="btn btn-primary float-right" type="submit">submit</button>
</div>
</div>
</form>
This is how you can do it with FormArray
component
import { Component, OnInit } from "@angular/core";
import {
Validators,
FormBuilder,
FormGroup,
FormControl,
FormArray
} from "@angular/forms";
@Component({
...
})
export class SurveyComponent implements OnInit {
public surveyForm: FormGroup;
constructor(protected formBuilder: FormBuilder) {}
ngOnInit() {
this.surveyForm = this.formBuilder.group({
Survey: new FormGroup({
Id: new FormControl("", Validators.required),
Name: new FormControl("", Validators.required)
}),
QuestionAnswerSets: new FormArray([])
});
}
initSet() {
return this.formBuilder.group({
Question: new FormControl("", Validators.required),
Answers: new FormArray([])
});
}
addSet() {
const questionAnswerSets = <FormArray>(
this.surveyForm.controls.QuestionAnswerSets
);
questionAnswerSets.push(this.initSet());
}
removeSet(setId: number) {
let questionAnswerSets = <FormArray>(
this.surveyForm.controls.QuestionAnswerSets
);
questionAnswerSets.removeAt(setId);
}
initAnswer() {
return this.formBuilder.group({
Answer: new FormControl("", Validators.required)
});
}
addAnswer(setId: number) {
const questionAnswerSets = <FormArray>(
this.surveyForm.controls.QuestionAnswerSets
);
const answer = <FormArray>(
questionAnswerSets.controls[setId]["controls"].Answers
);
answer.push(this.initAnswer());
}
removeAnswer(setId: number, answerId: number) {
const questionAnswerSets = <FormArray>(
this.surveyForm.controls.QuestionAnswerSets
);
const answer = <FormArray>(
questionAnswerSets.controls[setId]["controls"].Answers
);
answer.removeAt(answerId);
}
}
teamplate
<form [formGroup]="surveyForm" (ngSubmit)="saveForm(surveyForm.value)">
<div formArrayName="QuestionAnswerSets">
<fieldset *ngFor="let questionAnswerSet of surveyForm.controls.QuestionAnswerSets.controls; index as setId;" [formGroupName]="setId">
<input type="text" formControlName="Question" />
<div formArrayName="Answers">
<div class="ui-g-12" *ngFor="let answer of this.surveyForm.controls.QuestionAnswerSets.controls[setId].controls.Answers.controls; index as answerId;"
[formGroupName]="answerId">
<input type="text" formControlName="Answer" />
<button type="button" (click)="removeAnswer(setId, answerId)">Remove Answer</button>
</div>
</div>
<button type="button" (click)="addAnswer(setId)">Add Answer</button>
</fieldset>
</div>
<button type="button" (click)="addSet()">Add Set</button>
</form>
Probably I've missed something, but it will give you an idea
After reading so many articles on nested models template driven forms and official angular documentation i could say this can not be done using template driven forms as there no directive which represents an array. (grouping as how fieldset does for an object with ngModelGroup)
This could be done with angular reactive forms with FormArrayName
https://angular.io/api/forms/FormArrayName
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With