Since you are here, I assume that you love Reactive Forms (the same do I), but you caught yourself wondering if you can split a big component into smaller ones. The short answer is yes!
In this article, I will explain a relatively easy way to do this.
When we design a form in Reactive Forms, we start by defining its fields, and we end up having a Form Model. In our example, the Form Model will have two different sections to accommodate the example of the below figure.
Those sections, or to be precise, those FormGroup items, will be the basicInfo and the address.
To build this we will create three components. A parent component, that will have the FormModel, and two child components that will have the HTML template bindings.
The parent component
The parent component will have the FormModel with all the validations, either those are built-in, custom, async or dynamic validators.
We will start small by examining the FormModel without any validations. The code will look like below:
Let's also have a look at the template:
Note here that we bind in an <ng-container> element the userForm which is the root FormGroup instance. Other than this, we see that the HTML template is very lightweight. We only have the selectors of two child components.
But wait, how are we going to bind each component with its respective FormGroup?
For this example, we will use the FormGroupDirective that automagically gets access to the parent's FormModel.
Again, we will start small to understand how the FormGroupDirective works and progressively we will make it a bit more complex.
Let's see the child components to understand how this works.
The child components
The child components will inject the FormGroupDirective, with which we get access to the parent's FormModel. Yes, correct! A child component can have access to the parent's component FormGroup instance.
Let's see how this works:
In the ngOnInit method, we initialize the form variable with the rootFormGroup.control, which gives us the parent's FormModel.
To have a better understanding to this assignment, let's see what the
console.log(this.rootFormGroup.control.value) will output
As said, it has the complete parent's FormModel. Cool!
Now, let's see how to apply the bindings in the HTML template.
Notice the first two lines of the HTML. We use the [formGroup] and the formGroupName directives to bind a mat-card with a specific FormGroup instance.
Well, believe it or not, that's all it! We managed to split the components and have the bindings in child components.
The next step is to apply the bindings for the Address component and we will have a complete solution to our initial problem. For clarity, I won't write more details about the Address, but instead you will find in the bottom of this article a working example.
I hear you saying that you like this approach, but you do not like that we have to define the formGroupName directive in the child component.
Why do we have to do this here? Well, because the FormGroupDirective gave us access to the complete parent's FormModel.
There is an alternative, however. We can get the nested formGroup from the source code (component.ts).
This approach simplifies the HTML by removing the formGroupName directive.
I guess that we can improve this even more. How about if we make the child component unaware of the parent's nested FormGroup name?
Bare with me and before we see the updated code of the child component, let's see how the HTML of the parent's component will become.
This looks better in terms of the Reactive Forms API :).
The idea with this approach is to dynamically provide the name of the nested formGroup in each child component.
The question that arises is how to get the name of the nested formGroup in each child component? There are various ways to do this, but I guess there is no reason to make this complex. We can rely on a simple approach by using an @input decorator!
Much better! :)
Since we have the FormModel in the parent component, we can set any validation we like.
We can even set a validation dynamically on the Address component, based on a value from the Basic Info component.
In the working example below, you will see that the address.company FormControl is required if the basicInfo.age > 18.
Find a working example in this stackblitz link
You can also check the following video:
If you want to check more options, have a look at this link as well.
Thanks for reading this article!