How to make reusable forms in Angular


It's easier then you might think.

It's time for a dive into reusable Angular forms.

Today you'll learn how to reuse your form control or the group of form controls by extracting them into a separate component.

And then reuse this component in other Angular forms in your angular application.

what-is-that-what-is-happening

So pull out your favorite code editor and let's learn how to make reactive form controls reusable in your applications.

1. Create the reusable form group

We'll begin by generating a component that will hold our reusable form group.

ng generate component name-group

And then use this for the component's code.

import { Component, Input, inject } from '@angular/core';
import { ControlContainer, FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';

@Component({
  selector: 'app-name-group',
  standalone: true,
  imports: [ReactiveFormsModule],
  viewProviders: [
    {
      provide: ControlContainer,
      useFactory: () => inject(ControlContainer, {skipSelf: true})
    }
  ],
  template: `
      <fieldset [formGroupName]="controlKey">
        <legend>{{label}}</legend>
        <div class="form-field">
          <label for="firstName">First Name</label>
          <input formControlName="firstName" type="text" id="firstName">
        </div>
        <div class="form-field">
          <label for="lastName">Last Name</label>
          <input formControlName="lastName" type="text" id="lastName">
        </div>
      </fieldset>
  `
})
export class NameGroupComponent {
  @Input({ required: true }) controlKey = '';
  @Input() label = '';
  parentContainer = inject(ControlContainer);

  get parentFormGroup() {
    return this.parentContainer.control as FormGroup;
  }

  ngOnInit() {
    this.parentFormGroup.addControl(this.controlKey, 
      new FormGroup({
        firstName: new FormControl(''),
        lastName: new FormControl('')
      }));
  }
  ngOnDestroy() {
    this.parentFormGroup.removeControl(this.controlKey);
  }
}

2. Reuse the form group in our form

Now we're ready to reuse the form group inside of any form across our application.

So we'll open the app.component.ts file, or where ever you plan to use this form group.

import { Component } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { NameGroupComponent } from './address-group/address-group.component';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [ReactiveFormsModule, NameGroupComponent],
  template: `
    <form [formGroup]="form" (ngSubmit)="submit()">
      <app-name-group label="Name of Father" controlKey="nameOfFather"></app-name-group>
      <app-name-group label="Name of Mother" controlKey="nameOfFather"></app-name-group>
      <button>Submit</button>
    </form>
  `
})
export class AppComponent {
  form = new FormGroup({  });
  submit() {
    console.log(this.form.value);
    this.form.reset();
  }

}

And we'll want to add some styling for this example in our styles.scss file.

/* You can add global styles to this file, and also import other style files */
:root {
  --border-radius: 10px;
  --spacing-step: 8px;
  --color-background: #eceff1;
  --color-accent-secondary: #ffc15a;
  --color-accent-secondary-darker: #faa415;
  --color-text: #263238;
  --color-text-light: #607d8b;
}

body, html, p, h1, h2, h3, h4, h5, span {
  padding: 0;
  margin: 0;
}
body {
  font-family: 'Poppins';
  background-color: var(--color-background);
}
form {
  padding: calc(var(--spacing-step) * 3);
  box-sizing: border-box;
  border-radius: var(--border-radius);
  background: var(--color-background);
  min-width: 395px;
  width: 100%;
}

label, input:not([type="checkbox"]), fieldset, button {
  width: 100%;
  box-sizing: border-box;
}

fieldset {
  border-radius: var(--border-radius);
  border: 1px solid var(--color-text-light);
  margin-top: var(--spacing-step);
}

button {
  margin-top: calc(var(--spacing-step) * 2);
  background-color: var(--color-accent-secondary);
  border: var(--color-accent-secondary-darker) 1px solid;
  color: var(--color-text);
  font-weight: bold;
  cursor: pointer;
  &:hover {
    opacity: 0.8;
  }
  &:active {
    background-color: var(--color-accent-secondary-darker);
    border: var(--color-accent-secondary) 1px solid;
  }
  &.reset-button {
    background-color: transparent;
    border: var(--color-primary) 1px solid;
  }
}

input {
  border: var(--color-text) solid 2px;
}

legend {
  font-weight: bold;
}

input, button {
  border-radius: 5px;
  font-size: 18px;
  padding: 10px 11px;
}

Conclusion

And that, my friend, is how to easily and quickly reuse your Angular form groups across your entire Angular application.

If you want a deeper explanation as to how all this code works, I'd recommend this video on Youtube.

How to Make Forms in Angular REUSABLE

Till later,

signature

Daniel Kreider