Angular Reactive Forms: How to structure your nested forms

Angular Reactive Forms: How to structure your nested forms

Tráng Lê

Tráng Lê

·

Follow

·

May 19, 2019

3 min read

In this article, I will show you a simple way to implement Reactive Forms in your Angular project. I assume that you have basic knowledge about Angular Reactive Forms. I will mention some important points.

What is Reactive Forms?

“Reactive forms provide a model-driven approach to handling form inputs whose values change over time”
Another way to say that Reactive forms is form validation by code. Different than form validation directly on HTML template (Template-driven approach)

Form structure

FormControl is a basic block to build reactive forms, a place to do the validation. Can imagine it like a form unit.
FormGroup represents for form controls & form group inside it.
FormArray contains a list of form controls or form groups.

Organize your form

Let start with JSON structure below, as a practical example.

//Employee info
{
generalInfo: {
fullName: 'Trang Le',
address: 'Ho Chi Minh City, Viet Nam',
dateOfBirth: '1888-07-07',
phoneInfo: {
mobilePhone: '+84981111111',
homePhone: '+84982222222'
}
},
emergencyContacts: ['+84983333333', '+84984444444']
}

How we’re going to design our reactive form? Simple, start with FormControl as a form unit, usually it is the thing that you cannot break down into smaller pieces. Then consider whether its parent object is FormGroup or FormArray.

{ root => FormGroup
generalInfo: { => FormGroup
fullName: 'Trang Le', => FormControl
address: 'Ho Chi Minh City, Viet Nam', => FormControl
dateOfBirth: '1888-07-07', => FormControl
phoneInfo: { => FormGroup
mobilePhone: '+84981111111', => FormControl
homePhone: '+84982222222' => FormControl
}
},
emergencyContacts: [ => FormArray
'+84983333333', => FormControl
'+84984444444' => FormControl
]
}

On code snippet above, you can see FormControl is a property has primitive value. But this is just a simple case. FormControl can be an object, as long as you see it/its layout as a form unit. I can give you this example in my next topic: “Custom form control — control value accessor”.

Visualize the things on UI

Finally, what we have in Angular code.

//app.component.js
const
employeeFormGroup: FormGroup = this.formBuilder.group({
generalInfo: this.formBuilder.group({
fullName: ['Trang Le'], //shortcut of new FormControl('...')
address: ['Ho Chi Minh City, Viet Nam'],
dateOfBirth: ['1888-07-07'],
phoneInfo: this.formBuilder.group({
mobilePhone: ['+84981111111'],
homePhone: ['+84982222222']
})
}),
emergencyContacts: this.formBuilder.array([['+84983333333'], ['+84984444444']])
});

//app.component.html
<form [formGroup]="employeeFormGroup">
<app-general-info formGroupName="generalInfo"></app-general-info>
<app-emergency-contacts formArrayName="emergencyContacts"></app-emergency-contacts>
</form>

Thing is easy if you keep HTML template in one component, but what should you do if you would like to break down into smaller component like app.component.html above. I have a form contains app-general-info & app-emergency-contacts for reusable purpose. OK. Let’s dive into child component app-general-info.

//general-info.component.html
<ng-container [formGroup]="controlContainer.control">
<h3>General info</h3>
<div>Full name</div>
<input formControlName="fullName"/>
<div>Address</div>
<input formControlName="address"/>
<div>Date of birth</div>
<input formControlName="dateOfBirth" type="date"/>
<app-phone-info formGroupName="phoneInfo"></app-phone-info>
</ng-container>

//general-info.component.ts
export class GeneralInfoComponent {
constructor(public controlContainer: ControlContainer) {
}
}

The question is how can I take form element such as FormGroup or FormArray from parent form, then bind it in current component? The answer in app-general-info component is pretty simple: using Control Container & follow 3 steps:

  1. Bind from parent component

//app.component.html
<app-general-info formGroupName="generalInfo"></app-general-info>

2. Take current binding form control in child component

controlContainer.control

3. Bind to child component

//general-info.component.html
<ng-container [formGroup]="controlContainer.control">

One more look inside app-phone-info with same usage.

//general-info.component.html
<ng-container [formGroup]="controlContainer.control">
...
<app-phone-info formGroupName="phoneInfo"></app-phone-info>
...

//phone-info.component.html
<ng-container [formGroup]="controlContainer.control">
<h3>Phone info</h3>
<div>Mobile number</div>
<input formControlName="mobilePhone"/>
<div>Home number</div>
<input formControlName="homePhone"/>
</ng-container>

//phone-info.component.ts
export class PhoneInfoComponent {

constructor(public controlContainer: ControlContainer) {
}
}

You can clone Git repo to test the full source code 🙂 have fun!

Alternate Text Gọi ngay