I am trying to nest multiple reactive forms in my angular project and there forms are in different components.
For example I have a component that contains a form with two inputs one input for name and one input for description and a submit button. I call this component NameDescComponent
I am planning to use this component across multiple pages and forms. Here is the html for the component.
<form [formGroup]="nameDescForm" (ngSubmit)="customEmit()">
<div fxLayout="row" fxLayoutGap="10px" fxFlex>
<mat-form-field>
<input matInput placeholder="Name" formControlName="name">
</mat-form-field>
<mat-form-field fxFlex>
<input matInput placeholder="Description" formControlName="description">
</mat-form-field>
</div>
<div fxLayout="column" fxLayoutGap="10px">
<button type="submit" mat-raised-button color="primary">
{{buttonText}}
</button>
<div>
</div>
</div>
</form>
And here is the abbreviated ts file
public nameDescForm: FormGroup;
@Input() public buttonText: string;
@Output() public save: EventEmitter<any> = new EventEmitter<any>();
@Output() public nameDescFormEmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
constructor(fb: FormBuilder) {
this.nameDescForm = fb.group({
'name': ['', Validators.required],
'description': ['']
});
}
public ngOnInit() {
console.log(this.nameDescForm);
this.nameDescFormEmit.emit(this.nameDescForm);
}
public customEmit() {
this.save.emit();
}
Then in a page where I am using this component I also have another form with the NameDescComponent
inside the form, like this
<form [formGroup]="parentForm" (ngSubmit)="customEmit()">
<app-name-description (nameDescFormEmit)="getNameDescForm($event)" buttonText="Save" (save)="save()"></app-name-description>
<input type="test" formControlName="test">
</form>
Currently what I am doing is passing the nameDescFrom
from its component to the ParentComponent
with the Output and EventEmitter. This solution seems to work and when I update child I am able to access the values. But the downside is when I go to submit the form I have to check that the parentForm
and then nameDescFrom
are both valid and manage both forms separately.
I am wondering if there is a better way to approach this? When I can access the nameDescFrom
from within the parent form?
Thanks
I am trying to nest multiple reactive forms in my angular project and there forms are in different components.
For example I have a component that contains a form with two inputs one input for name and one input for description and a submit button. I call this component NameDescComponent
I am planning to use this component across multiple pages and forms. Here is the html for the component.
<form [formGroup]="nameDescForm" (ngSubmit)="customEmit()">
<div fxLayout="row" fxLayoutGap="10px" fxFlex>
<mat-form-field>
<input matInput placeholder="Name" formControlName="name">
</mat-form-field>
<mat-form-field fxFlex>
<input matInput placeholder="Description" formControlName="description">
</mat-form-field>
</div>
<div fxLayout="column" fxLayoutGap="10px">
<button type="submit" mat-raised-button color="primary">
{{buttonText}}
</button>
<div>
</div>
</div>
</form>
And here is the abbreviated ts file
public nameDescForm: FormGroup;
@Input() public buttonText: string;
@Output() public save: EventEmitter<any> = new EventEmitter<any>();
@Output() public nameDescFormEmit: EventEmitter<FormGroup> = new EventEmitter<FormGroup>();
constructor(fb: FormBuilder) {
this.nameDescForm = fb.group({
'name': ['', Validators.required],
'description': ['']
});
}
public ngOnInit() {
console.log(this.nameDescForm);
this.nameDescFormEmit.emit(this.nameDescForm);
}
public customEmit() {
this.save.emit();
}
Then in a page where I am using this component I also have another form with the NameDescComponent
inside the form, like this
<form [formGroup]="parentForm" (ngSubmit)="customEmit()">
<app-name-description (nameDescFormEmit)="getNameDescForm($event)" buttonText="Save" (save)="save()"></app-name-description>
<input type="test" formControlName="test">
</form>
Currently what I am doing is passing the nameDescFrom
from its component to the ParentComponent
with the Output and EventEmitter. This solution seems to work and when I update child I am able to access the values. But the downside is when I go to submit the form I have to check that the parentForm
and then nameDescFrom
are both valid and manage both forms separately.
I am wondering if there is a better way to approach this? When I can access the nameDescFrom
from within the parent form?
Thanks
Share Improve this question asked Jan 9, 2019 at 11:53 ghjghgkjghjghgkj 3931 gold badge3 silver badges9 bronze badges 3- you could include the child form into the parent form. Then you pass the form.get('<child-form-groupname>') to the child by binding. So you won't need to manage the forms separately. – Gérôme Grignon Commented Jan 9, 2019 at 12:12
- If your form.value is like {name:..,description:...,others.properties} not {othersproperties:..,group:{name:..,description:...}} use FormgroupDirective like stackoverflow.com/questions/54073280/… – Eliseo Commented Jan 9, 2019 at 12:29
- Hi, we (at work) have been working on that topic and I gave a proper answer you may want to check here stackoverflow.com/a/55457210/2398593 :) – maxime1992 Commented Apr 1, 2019 at 14:16
2 Answers
Reset to default 13To merge your form with nested forms and have a single validation process for all of them, you can use the formbuilder
to create the whole model object structure in the root form component.
Then in its html template you will add the custom element of the sub-forms (ex: <nested-form>
), which will render the sub-forms.
See example : https://stackblitz.com/edit/angular-m5fexe)
Useful angular doc links :
- https://angular.io/guide/reactive-forms#creating-nested-form-groups
- https://angular.io/guide/reactive-forms#step-3-generating-form-controls
Code :
export class Form1Component {
@Input() name: string;
public dummyForm: FormGroup;
constructor(
private _fb: FormBuilder,
) {
this.createForm();
}
createForm() {
this.dummyForm = this._fb.group({
username: ['username', Validators.required],
nestedForm: this._fb.group({
complement1: ['complement1', Validators.required],
complement2: ['complement2', Validators.required],
})
});
}
submit() {
if (this.dummyForm.valid) {
console.log('form AND subforms are valid', this.dummyForm.value);
} else {
console.warn('form AND/OR subforms are invalid', this.dummyForm.value);
}
}
}
Template for the Form1Component :
<form [formGroup]="dummyForm" (ngSubmit)="submit()">
<div>
<label for="username">Root Input</label>
<input type="text" id="username" formControlName="username"/>
</div>
<nested-form [parentForm]="dummyForm"></nested-form>
<button>Send</button>
</form>
Nested form code:
export class NestedFormComponent {
@Input()
public parentForm: FormGroup;
}
Nested form template :
<form [formGroup]="parentForm">
<div formGroupName="nestedForm">
<div>
<label for="complement1">Nested input 1</label>
<input type="text" formControlName="complement1"/>
</div>
<div>
<label for="complement1">Nested input 1</label>
<input type="text" formControlName="complement2"/>
</div>
</div>
</form>
You can also use a custom form control, it is a built-in concept in angular.
The idea is to have a custom component that will serve as a "bridge" between the parent form and the nested form. the custom component will listen to "change" event of the nested form and will update its value with it. the parent form treats the custom control as a single field although its value will be an object containing the nested form controls values.
The custom conponent should implenet an interface called ControlValueAccessor, by which angular provides all neccesary to update the custom control states such as value, valid, touched, pristine and so on. in this way you actually manage the custom control state so that the parent form can treat it like any other form control.
You can read about it in the following links:
https://blog.sreyaj.dev/custom-form-controls-controlvalueaccessor-in-angular
https://medium.com/@majdasab/implementing-control-value-accessor-in-angular-1b89f2f84ebf
https://indepth.dev/posts/1055/never-again-be-confused-when-implementing-controlvalueaccessor-in-angular-forms