Tech Incent
Angular

Create forms using angular FormBulder

form builder

What is angular formbuilder?

FormBuilder is in-build service class of angular reactive form. FormBuilder provides (group, control, array) methods to create FormGroup, FormControl, FormArray blocks that are instances of Angular Reactive Form.

Some of the advantages of FormBuilder

  • Shortly creating instances of FormControl, FormGroup, or FormArray
  • Reduce the repeatation of FormControl, FormGroup, FormArray class.
  • Reduces the required code to build the complex forms

How to use angular formbuilder?

  1. Import ReactiveFormsModule module.
  2. Inject FormBuilder in component.
  3. Now create a form using formBuilder

FormBuilder control/FormControl

Control can be defined with [ ] symbol inside FormBulder group or array and that takes the first parameter as default value. and second as validation class or array of validation classes. example

["defaultValue", Validators.required]
[54, [Validators.required, Validators.min(1)]] // Multiple validation

define with the method but inside FormGroup and FormArray, it’s not a good approach and not recommended

name: FormControl = this.formBuilder.control('', Validators.required)

Create productForm using FormBuilder step-by-step

Step-1 Setting up new project

Generate new angular project

ng new angular-reactive-formbuilder --routing=true --style=scss --skip-tests
cd angular-reactive-formbuilder

That will generate a new angular project with routing, the style set to scss (available style CSS, SASS, SCSS, LESS), and skip test configuration.

Import ReactiveFormsModule modules

The reactive forms require the import ReactiveFormsModule module. Update app.module.ts file, add ReactiveFormsModule module.

import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Step-2 Define productForm in component class

Edit app.component.ts file

import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  categories: string[] = ['Accessories', 'Apparel', 'Footwear', 'Homewares', 'Audiobooks']
  productForm!: FormGroup;
  constructor(private formBuilder: FormBuilder) {}
  ngOnInit(): void {
    this.productForm = this.formBuilder.group({
      name: this.formBuilder.control('', Validators.required),
      price: ['', [Validators.required, Validators.min(1)]],
      description: ['',],
      category: ['', [Validators.required]],
      keywords: this.formBuilder.array([
        ['', [Validators.required]],
        ['', [Validators.required]],
        ['', [Validators.required]],
      ])
    })
  }
  get keywordsControls(): any {
    return (<FormArray>this.productForm.get('keywords')).controls;
  }
  onFormSubmit(): void {
    const formData = this.productForm.value
    console.log(formData)
    // Call api post service here
  }
}

let’s breakdown app.component.ts:

Product form has name, price, description, category as FormControl. and keywords as FormArray which also has three controls.

Don’t be confused about the name, price control declaration. Name, price both are the same as FormControl. I always use the price way, it’s shorts and the same.

keywordsControls provide keywords FormArray separately. and rest of OnFormSubmit is a form submit methods

Step-3 Create product form and bind in html

update app.component.html file

<div class="container">
  <form [formGroup]="productForm" (ngSubmit)="onFormSubmit()">
    <h3>Product Form</h3>
    <div class="input-group">
      <input [class.border-danger]="productForm.get('name')?.touched && productForm.get('name')?.invalid" type="text" formControlName="name" placeholder="Name">
      <div *ngIf="productForm.get('name')?.touched && productForm.get('name')?.hasError('required')" class="text-sm text-danger">Product name is required</div>
    </div>
    <div class="input-group">
      <input [class.border-danger]="productForm.get('price')?.touched && productForm.get('price')?.invalid" type="email" formControlName="price" placeholder="Price">
      <div *ngIf="productForm.get('price')?.touched && productForm.get('price')?.hasError('required')" class="text-sm text-danger">Price is required</div>
      <div *ngIf="productForm.get('price')?.touched && productForm.get('price')?.hasError('min')" class="text-sm text-danger">Min 1 required</div>
    </div>
    <div class="input-group">
      <select formControlName="category" [class.border-danger]="productForm.get('number')?.touched && productForm.get('number')?.invalid">
        <option [value]="''">Select Category</option>
        <option [value]="cat" *ngFor="let cat of categories">{{cat}}</option>
      </select>
      <div *ngIf="productForm.get('category')?.touched && productForm.get('category')?.hasError('required')" class="text-sm text-danger">Category is required</div>
    </div>
    <div class="input-group">
      <textarea formControlName="description" id="" cols="30" rows="10" placeholder="Description"></textarea>
    </div>
    <hr>
    <h4>Keywords</h4>
    <div formArrayName="keywords">
      <div *ngIf="keywordsControls && keywordsControls.length">
        <div *ngFor="let keywordControl of keywordsControls; let i = index">
          <div class="input-group">
            <input [class.border-danger]="keywordControl.touched && keywordControl.invalid" [formControlName]="i" type="text" placeholder="keyword" />
            <div *ngIf="keywordControl.touched && keywordControl.hasError('required')" class="text-sm text-danger">keyword Name is required</div>
          </div>
          <hr>
        </div>
      </div>
    </div>
    <button [disabled]="productForm.invalid" style="margin-top: 40px;" type="submit">Submit</button>
  </form>
</div>

Step-4 Add minor css for UI form (optional)

CSS is totally UI representational. it does not affect angular ReactiveForm but Effect in HTML Form and HTML validation error message style

* {
  box-sizing: border-box;
}
.container {
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
  padding-top: 50px;
}
.text-sm {
  font-size: 80%;
  margin-top: 5px;
}
.border-danger {
  border-color: red;
}
.text-danger {
  color: red;
}
textarea,
select,
input {
  width: 100%;
  padding: 5px 12px;
}
.flex {
  display: flex;
}
.items-center {
  align-items: center;
}
.justify-end {
  justify-content: flex-end;
}
// form
.input-group {
  margin-bottom: 10px;
}

Angular FormBuilder official API ref https://angular.io/api/forms/FormBuilder

Related posts

Angular Pure Bootstrap 5 Sidenav

Sajal Mia

Angular JWT token authentication (login, auth and error interceptors, guard, protected route) example: Step by step

Sajal Mia

Code Syntax Highlighter Angular With Prism.js

Sajal Mia

Angular material data table, sort, pagination, filter – complete example

Sajal Mia

How to implement angular router resolver?

Sajal Mia

Implement angular (product) details page with reactive service

Sajal Mia