I have found this guide to implement sub-forms in Angular.
My parent form holding all the child forms looks like this:
export class ParentFormComponent implements OnInit {
valueArray: Array<string> = [
'value1',
'value2',
'value3',
];
@ViewChild(ChildFormComponent, {static: true}) childFormComponent: ChildFormComponent;
//more ViewChildren with childFormComponents
parentForm: FormGroup = this.fb.group({});
constructor(private fb: FormBuilder) {
}
ngOnInit(): void {
this.parentForm= this.fb.group({
childformgroup: this.childFormGroup.createGroup(this.valueArray),
//more childFormGroups
})
}
}
This is the ChildFormComponent nested in the parent Form:
export class ChildFormComponent implements OnInit {
valueArray: Array<string> = [];
childForm: FormGroup = this.fb.group({});
constructor(private fb: FormBuilder) {
}
updateValidation(arrayValueCheckbox: Checkbox) {
//code to add or remove Validators for FormControls
}
createGroup(valueArray: Array<string>) {
this.valueArray= valueArray;
this.addFormControls(valueArray);
return this.childForm;
}
//create some FormGroup Elements
addFormControls(valueArray: Array<string>) {
this.valueArray.forEach(arrayValue=> {
this.childForm.addControl(arrayValue + 'Checkbox', new FormControl());
this.childForm.addControl(arrayValue + 'Calendar', new FormControl({ value: '',
disabled: true }));
this.childForm.addControl(arrayValue + 'Textbox', new FormControl({ value: '',
disabled: true }));
});
}
ngOnInit(): void {
}
}
The parent HTML:
<input type="checkbox" ... #checkbox>
<div class="fadein" [hidden]="!checkbox.checked">
<childform-omponent></childform-omponent>
</div>
This method works quite well, but it has some flaws:
The validation of the parent form will still be invalid if the childForm is invalid. For example: If the user does not check the checkbox and does not fill the required Formcontrols of the childForm
The childForm component will be rendered, even if i don't need it. It's just hidden.
On the other hand, the changed values from the child input fields (textbox etc.) will still be present, if the parent checkbox will be unchecked and checked again.
Currently i am trying to find a solution using the ng-container. This would solve the second flaw mentioned before:
<input type="checkbox" ... #checkbox>
<ng-container *ngIf="!checkbox.checked">
<childform-component></childform-component>
</ng-container>
Using the ng-container, the childform will not be loaded beforehand, so the createGroup function will thorw an error:
childformgroup: this.childFormGroup.createGroup(this.valueArray),
At this point i have no clue how to implement my dynamic nested forms with the ng-container and dynamically add the necessary formgroup to the parents form.
Questions:
What do I have to change in my current implementation if I want to make use of the ng-container?
How can I call the createGroup() function from my child components when it's not loaded yet?
Whats the best approach to add and remove childFormGroups dynamically to my ParentForm?
I want to keep the input values if the child has been destoryed (checkbox checked -> added values to input fields -> checkbox checked twice -> values should still exist)
The Validation has to be dynamic based on the childformgroups.
To be honest: I have messed around with lifecycle hooks, References and some other stuff. Nothing did the trick.
(Could you please paste the full error message you got when attempting to create the child's formGroup? It might help debug the issue :) )
From my understanding, if you'd like to dynamically render the child component using
ng-container+*ngIf, we need to make sure that the parent component has access to theChildFormComponentby the time we call the child'screateGroup()method.So instead of instantiating all
formGroupsin the parent component'sngOnInit, I'd build only theparentFormfirst, and then dynamically add thechildformgroupwhenever the checkbox input is checked.For that, you can make use of the
(change)event (which will also help with change detection) to call a method that will handle toggling the form and dynamically adding thechildformgroupto theparentForm.So your parent component's template would look something like:
Now for the changes in your parent component's logic:
@ViewChildrendecorator. It provides thechangesObservable, which you can use to make sure that the child component is accessible;childformgroup;Here's how it'd look like:
Hopefully this answers questions 1) and 2).
For 3.1) I believe you'd have to handle this manually; I suggest creating a property to store the current values in the parent component. Since we now have access to the child component's form, you can make use of the FormGroup's
valueChangesObservable to get the current values. Then we can pass these values down as an additional argument tocreateGroup()and build the new form with the stored values.3.2) since the parent component now handles creating and removing the
childformgroupentirely, the validation can be set when creating theFormControl's, like: