How to parse your reactive forms into a smaller piece in Angular
Before starting to article, it would be nice if you know these topics
ViewProviders
metadata of@Component
decorator.@Host
resolution modifier.
If you are not familiar with these topics you can read these articles.
The main idea of this blog post is how to parse your reactive forms template with small components.
For simplicty i will use a very simple basic form, use this pattern for creating reusability or parsing big reactive forms.
I would like the produce the error so you can see what you get when you make this error 🙂
Imagine you have this kind of component
import { Component, inject } from '@angular/core';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="save()">
<div class="form-group">
<label for="name">Name</label>
<input
type="text"
id="name"
formControlName="name"
class="form-control"
/>
</div>
<div class="form-group">
<label for="surname">Surname</label>
<input
type="text"
id="surname"
formControlName="surname"
class="form-control"
/>
</div>
<button type="submit" class="btn btn-primary">Save</button>
</form>
`,
})
export class AppComponent {
protected readonly fb = inject(FormBuilder);
form = this.fb.group({
name: [''],
surname: [''],
});
save() {
console.log(this.form.value);
this.form.reset();
}
}
- And you want to take the surname input in a seperate component, lets do that.
- Remove surname input and paste it to the newly created child-component.
child.component.ts
import { Component } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-child',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<div class="form-group">
<label for="surname">Surname</label>
<input
type="text"
id="surname"
formControlName="surname"
class="form-control"
/>
</div>
`,
})
export class ChildComponent {}
- Add
app-child
component to appropriate place. - You will get this error;
Before the solution let’s investigate why this error occurs.
If we check the source code of formControlName
directive, we will see this line of code.
formControlName
directive expecting a ControlContiner with the@Host
resolution modifier. This means it is searchingControlContainer
in the current view. But theControlContainer(formGroup)
doesn’t exist in newly created component.- As you know if we want to get some dependency with the
@Host
resolution modifer which is not exist in current view of component, we can make some tricks with viewProviders.
Now i will add viewProviders
to newly created ChildComponent
.
import { Component, inject } from '@angular/core';
import { ControlContainer, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-child',
standalone: true,
imports: [ReactiveFormsModule],
template: `
<div class="form-group">
<label for="surname">Surname</label>
<input
type="text"
id="surname"
formControlName="surname"
class="form-control"
/>
</div>
`,
viewProviders: [
{
provide: ControlContainer,
useFactory: () => inject(ControlContainer, { skipSelf: true }),
},
],
})
export class ChildComponent {}
- Thats all, now it works perfectly.