December 3, 2018
Nested Forms in Angular
This Article is based on Kara Ericksons talk about “Angular Forms” at the Angular Connect 2017.
I am going to give you a quick example to get started with nested Forms.
TL;DR
- Extract the HTML in new Component
- Inject ControlContainer in new Component
- Access the Form via ControlContainer.control
- pass the parent Form into the nested Component with the [formGroup] directive
- StackBlitz Example
Basic Form
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<form [formGroup]="form" (ngSubmit)="onSubmit()"> <p>General:</p> <label for="name">Name: </label> <input id="name" type="text" formControlName="name"> <p>Address:</p> <label for="street">Street: </label> <input id="street" type="text" formControlName="street"> <br> <label for="zip">ZIP: </label> <input id="zip" type="text" formControlName="zip"> <br> <label for="city">City: </label> <input id="city" type="text" formControlName="city"> <br> <button type="submit">Save</button> </form> |
We have a classic form to enter the name and the address of a person. Now we want to reuse the address block in other forms.
Nested Forms
Extracting the Form in a new Component is quite basic. We move the needed HTML into a new Component and wrap it in an <ng-container [formGroup]=”controlContainer.control”>.
1 2 3 4 5 6 7 8 9 |
<ng-container [formGroup]="controlContainer.control"> <p>Address:</p> <label for="street">Street: </label> <input id="street" type="text" formControlName="street"> <br> <label for="zip">ZIP: </label> <input id="zip" type="text" formControlName="zip"> <br> <label for="city">City: </label> <input id="city" type="text" formControlName="city"> <br> </ng-container> |
In the Component class we need inject the ControlContainer, this will give us the access to the given FormGroup from the parent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import { Component, OnInit } from '@angular/core'; import { ControlContainer } from '@angular/forms'; @Component({ selector: 'app-form-address', templateUrl: './address.component.html', styleUrls: ['./address.component.css'] }) export class AddressComponent implements OnInit { constructor(private controlContainer: ControlContainer) { } ngOnInit() { } } |
In the parent component, we need to replace the old HTML with the new component tag.
1 |
<app-form-address [formGroup]="form"></app-form-address> |
Don’t forget to add the [formGroup] Attribute and pass in a valid FormGroup.
If you try the StackBlitz example, you can see that after you press “save” the form with the nested Form results will be printed to the screen.
Troubleshooting
Error:
ERROR Error: formControlName must be used with a parent formGroup directive. You’ll want to add a formGroup
directive and pass it an existing FormGroup instance (you can create one in your class).
Solution:
Add a [formGroup] as attribute to your nested form component call.
Error:
ERROR TypeError: Cannot read property ‘get’ of undefined
Solution:
Inject the ControlContainer to the nested form component.
Nice post, thank you for the example. It worked well for us.