DEV Community

Balaji
Balaji

Posted on

⚡️Understanding angular dynamic forms with JSON data

Angular is a powerful and popular front-end framework that allows developers to build dynamic and interactive web applications. One of Angular's key features is its ability to seamlessly process modules. In this blog post, we will explore the concept of dynamic forms in Angular and explore how JSON can be utilized to create dynamic and flexible forms.

What are Dynamic Forms?

Dynamic forms in Angular are forms that are generated dynamically at runtime based on certain conditions or data. Unlike static forms, where the structure is predefined in the HTML, dynamic forms are more flexible and can adapt to changing requirements. This is particularly useful in scenarios where the form structure needs to be determined dynamically based on user input, API responses, or other dynamic data sources.

Creating a Simple Dynamic Form

Let's walk through a basic example of creating a dynamic form using Angular and JSON.

Step 1: Set Up Your Angular Project
Assuming you have Angular CLI installed, create a new Angular project:

ng new dynamic-forms
cd dynamic-forms
Enter fullscreen mode Exit fullscreen mode

Step 2: Install Required Dependencies
Install the Angular Reactive Forms module, which will be used to create and manage dynamic forms:

ng generate module dynamic-form
Enter fullscreen mode Exit fullscreen mode

Step 3: Define interface
Create a file named form.interface.ts to define the structure of your dynamic form interface:

export interface IFormStructure {
  type: string;
  label: string;
  name: string;
  value: string | number | boolean;
  options?: { label: string; value: number | string | boolean }[];
  validations?: {
    name: string;
    validator: string;
    message: string;
  }[];
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Define Form Structure in JSON
Create a file named form-config.ts to define the structure of your dynamic form:

import { IFormStructure } from './form.interface';

export const formConfig: IFormStructure[] = [
  {
    type: 'text',
    label: 'Name',
    name: 'name',
    value: '',
    validations: [
      {
        name: 'required',
        validator: 'required',
        message: 'Name is required',
      },
    ],
  },
  {
    type: 'textarea',
    label: 'Description',
    name: 'description',
    value: '',
    validations: [
      {
        name: 'required',
        validator: 'required',
        message: 'Description is required',
      },
    ],
  },
  {
    type: 'number',
    label: 'Age',
    name: 'age',
    value: '',
    validations: [],
  },
  {
    type: 'radio',
    label: 'Gender',
    name: 'gender',
    value: true,
    options: [
      { label: 'Male', value: true },
      { label: 'Female', value: false },
    ],
    validations: [],
  },
  {
    type: 'select',
    label: 'Country',
    name: 'country',
    value: 1,
    options: [
      { label: 'India', value: 1 },
      { label: 'USA', value: 2 },
      { label: 'Canada', value: 3 },
    ],
    validations: [
      {
        name: 'required',
        validator: 'required',
        message: 'Address is required',
      },
    ],
  },
];
Enter fullscreen mode Exit fullscreen mode

Step 5: Create Dynamic Form Component
Generate a new component to handle the dynamic form:

ng generate component dynamic-form
Enter fullscreen mode Exit fullscreen mode

Step 6: Dynamically Generate Form
Update the dynamic-form.component.ts file to dynamically generate the form based on the JSON configuration:

import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { IFormStructure } from './form.interface';
import { formConfig } from './form-config';

@Component({
  selector: 'app-dynamic-form',
  templateUrl: './dynamic-form.component.html',
  styleUrls: ['./dynamic-form.component.scss'],
})
export class DynamicFormComponent implements OnInit {
  formStructure: IFormStructure[] = formConfig;

  dynamicForm: FormGroup = this.fb.group({});

  constructor(private fb: FormBuilder) {
    let formGroup: Record<string, any> = {};
    this.formStructure.forEach((control) => {
      let controlValidators: Validators[] = [];

      if (control.validations) {
        ``;
        control.validations.forEach(
          (validation: {
            name: string;
            validator: string;
            message: string;
          }) => {
            if (validation.validator === 'required')
              controlValidators.push(Validators.required);
            if (validation.validator === 'email')
              controlValidators.push(Validators.email);
            // Add more built-in validators as needed
          }
        );
      }

      formGroup[control.name] = [control.value || '', controlValidators];
    });

    this.dynamicForm = this.fb.group(formGroup);
  }

  getErrorMessage(control: any) {
    const formControl = this.dynamicForm.get(control.name);

    if (!formControl) {
      return '';
    }

    for (let validation of control.validations) {
      if (formControl.hasError(validation.name)) {
        return validation.message;
      }
    }

    return '';
  }

  onSubmit() {
    if (!this.dynamicForm.valid) {
      this.dynamicForm.markAllAsTouched();
      return;
    }
    console.log(this.dynamicForm.value);
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Import bootstrap.css in style.css/scss file

@import "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css";
Enter fullscreen mode Exit fullscreen mode

Step 8: Display the Form in the Template
Update the dynamic-form.component.html file to display the dynamic form:

<div class="card">
  <div class="card-body">
    <form [formGroup]="dynamicForm" (ngSubmit)="onSubmit()">
      <div class="row">
        <div *ngFor="let control of formStructure" class="col-3">
          <!-- Text, Textarea & Number -->
          <div
            class="mb-3"
            *ngIf="['text', 'number', 'textarea'].includes(control.type)"
          >
            <label for="exampleFormControlInput1" class="form-label">
              {{ control.label }}
            </label>
            <input
              [type]="control.type"
              class="form-control"
              [formControlName]="control.name"
              *ngIf="control.type !== 'textarea'"
            />
            <textarea
              class="form-control"
              [formControlName]="control.name"
              *ngIf="control.type === 'textarea'"
            >
            </textarea>
            <span
              class="error"
              *ngIf="
                dynamicForm.controls[control.name]?.invalid &&
                dynamicForm.controls[control.name]?.touched
              "
            >
              {{ getErrorMessage(control) }}
            </span>
          </div>

          <!-- Radio Button -->
          <div class="mb-3" *ngIf="['radio'].includes(control.type)">
            <div>
              <label for="exampleFormControlInput1" class="form-label">
                {{ control.label }}
              </label>
            </div>
            <div
              class="form-check form-check-inline"
              *ngFor="let option of control.options"
            >
              <input
                class="form-check-input"
                type="radio"
                [formControlName]="control.name"
                id="inlineRadio1"
                [value]="option.value"
              />
              <label class="form-check-label" for="inlineRadio1">
                {{ option.label }}</label
              >
            </div>
          </div>

          <!-- Select  -->
          <div class="mb-3" *ngIf="['select'].includes(control.type)">
            <label for="exampleFormControlInput1" class="form-label">
              {{ control.label }}
            </label>
            <select
              class="form-select"
              aria-label="Default select example"
              [formControlName]="control.name"
            >
              <option selected>-Select-</option>
              <option
                *ngFor="let option of control.options"
                [value]="option.value"
              >
                {{ option.label }}
              </option>
            </select>
            <span
              class="error"
              *ngIf="
                dynamicForm.controls[control.name]?.invalid &&
                dynamicForm.controls[control.name]?.touched
              "
            >
              {{ getErrorMessage(control) }}
            </span>
          </div>
        </div>
      </div>

      <button type="submit" class="btn btn-primary">Submit</button>
    </form>
  </div>
</div>

Enter fullscreen mode Exit fullscreen mode

Step 9: Integrate the Dynamic Form Component
Integrate the DynamicFormComponent into your main application by adding it to the app.component.html file or any other desired location.

<app-dynamic-form></app-dynamic-form>
Enter fullscreen mode Exit fullscreen mode

Step 10: Run Your Angular Application
Run your Angular application to see the dynamically generated form in action:

ng serve
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:4200/ in your browser to interact with the dynamic form.

Output

dynamicform

Conclusion

In this blog post, we explored the concept of dynamic forms in Angular and how JSON can be leveraged to define the structure of these forms. By using Angular Reactive Forms and dynamically generating form controls based on JSON configuration, developers can create flexible and adaptive forms that cater to diverse requirements. This approach enhances maintainability and makes it easier to handle dynamic form scenarios in Angular applications.

Top comments (0)