How to parse your reactive forms into a smaller piece in Angular

Sinan Öztürk
3 min readMar 14, 2024

--

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.

ViewProviders, @Host

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 searching ControlContainer in the current view. But the ControlContainer(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.

--

--