How to make nested form in the way from angular.io documentation:
https://stackblitz.com/angular/pbdkbbnmrdg
Goal is to have two DropdownQuestion from question.service.ts in child formgroup named "details" and everything else like now in parent form...So at the end to look something like this:
{
"firstName":"Bembasto",
"email":"something@email.com",
"details": {
"dropdown1":{
},
"dropdown2":{
}
}
}
Uros, you need undestand how the code create the FormGroup and how create the inputs.
We has a complex object that it's used to
First we are going to create a new type of control question-group
import { QuestionBase } from './question-base';
export class GroupQuestion extends QuestionBase<string> {
controlType = 'group';
type: string;
constructor(options: {} = {}) {
super(options);
}
}
And add a new propertie to question-base
questions:any[];
//and change the constructor to allow give value
constructor(options: {
value?: T,
...
questions?:any[]
} = {}) {
this.value = options.value;
...
this.questions = options.questions || [];
}
Take a look how the code create the form. It's made in the question-control.service. Change the function toFormGroup to take account the typeControl "group"
toFormGroup(questions: QuestionBase<any>[] ) {
let group: any = {};
questions.forEach(question => {
group[question.key] = (question.controlType=='group')?
this.toFormGroup(question.questions)
:question.required ? new FormControl(question.value || '', Validators.required)
: new FormControl(question.value || '');
});
return new FormGroup(group);
}
Yes, we are using a recursive function. The idea we have is that we are going to have a question object like, e.g.
let questions: QuestionBase<any>[] = [
new DropdownQuestion({
...
}),
new TextboxQuestion({
...
})
, new GroupQuestion(
{
key: 'details',
label: 'Details',
order: 2,
questions: [
new TextboxQuestion({
...
}),
new DropdownQuestion({
...
})
]
}
)
];
Well, with this changes we have yet how create the formGroup but, how we show the inputs? Before, we are going to change the dinamic-form-component to allow pass as argument the "form"
@Input() form: FormGroup;
subGroup:boolean=true;
ngOnInit() {
if (!this.form)
{
this.form = this.qcs.toFormGroup(this.questions);
this.subGroup=false;
}
}
We add a new propertie "subGroup" to indicate if is a subgroup or not. So, we can hide the button "submit".
At last we are change the dynamic-form-question.component.html to take account the "group-questions"
<div [formGroup]="form">
<label [attr.for]="question.key">{{question.label}}</label>
<div [ngSwitch]="question.controlType">
<input *ngSwitchCase="'textbox'" ...>
<select *ngSwitchCase="'dropdown'" ...>
</select>
<div *ngSwitchCase="'group'" [formGroupName]="question.key">
<app-dynamic-form [form]="form.get(question.key)"
[questions]="question.questions"></app-dynamic-form>
</div>
</div>
<div class="errorMessage" *ngIf="!isValid">{{question.label}} is required</div>
</div>
Yes, if we has a group-question, we show an app-dinamic-form pass as form "form.get(question.key)". It's the reason we changed the dinamic-form-component: to allow pass a formGroup and only create a new form if not pass the value.
In this stackblitz are the full example
NOTE: Personally I don't like the component create the formGroup. I like create the formGroup in the main.component and pass as argument
in this other stackblitz explore this idea. The app-component has an ngOnInit that make the two calls
ngOnInit()
{
this.questions = this.service.getQuestions();
this.form=this.qcs.toFormGroup(this.questions);
}
And we need give value to the propertie "subGroup" of dinamic-form manually