How to Build Dynamic Forms in Angular
Tags: angular, javascript, frontend, forms
Published: 2022-11-14 08:31:05

When we build Angular Apps, the creation and build of forms is a manual process. The maintenance for small changes like adding a new field or switching field type from input to a date field should be easy, but why not create forms to be flexible and respond to business changes?

These changes require we touch the form again to update the hard-coded form declaration and fields. Why not change to make our form dynamic and not hard-coded?

Today we will learn how to create forms using reactive forms from a model and bind them to inputs, radio, and checkboxes in the template dynamic.

*I know the code needs types and interfaces, but I want to make a manageable article*.

## Scenario

We work for the marketing team, which wants a form to request the users Firstname, Lastname, and age. Let's build it.

First, declare a `FormGroup` field `registerForm` and create the method `buildForm()` to manually add every field in the formGroup.

```typescript
import { Component, OnInit, VERSION } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  registerForm: FormGroup;

  ngOnInit() {
    this.buildForm();
  }

  buildForm() {
    this.registerForm = new FormGroup({
      name: new FormControl(''),
      lastName: new FormControl(''),
      age: new FormControl(''),
    });
  }
}
```

Add the HTML markup with the inputs linked with the form using the `[formGroup]` directive.

```html
<h1>Register</h1>
<form [formGroup]="registerForm">
  <label>Name:<input type="text" formControlName="name" /></label>
  <label>LastName: <input type="text" formControlName="lastName" /></label>
  <label> Age: <input type="number" formControlName="age" /></label>
</form>
```

And Finally, we have our static forms!


But tomorrow, marketing wants to request the address or a checkbox for the newsletter. We need to update the form declaration, add the `formControl` and the inputs, and you know the whole process.

We want to avoid repeating the same task every time. We need to turn on the form to dynamic and react to business changes without touching the html or typescript file again.

## Create Dynamic FormControl and Inputs

First, we will have two tasks to build our form dynamic.

* Create the Form Group from the business object.
    
* Show the list of fields to render in the form.
    

First, rename `registerModel` to `model` and declare the fields  array. It will have all elements with inputs model for our dynamic form:

```typescript
fields: [];
model = {
        name: '',
        lastName: '',
        address: '',
        age: '',
 };
```

Next, create the method `getFormControlFields()`, with the`formGroupFields` object and Iterate over all properties in the model using the `for`, to push in `formGroupFields`. Add every field into the `fields` array.

The code looks like this:

```typescript
 getFormControlsFields() {
        const formGroupFields = {};
        for (const field of Object.keys(this.model)) {
            formGroupFields[field] = new FormControl("");
            this.fields.push(field);
        }
        return formGroupFields;
    }
```

### HOLD A SECOND! Do you want to learn to build complex forms and form controls quickly??

[Check out the Advanced Angular Forms & Custom Form Control Masterclass by Decoded Frontend.](https://courses.decodedfrontend.io/courses/advanced-angular-forms)

[![Learn Advanced Angular Forms Build](https://cdn.hashnode.com/res/hashnode/image/upload/v1670243926623/lGqVE5Gps.png align="left")](https://courses.decodedfrontend.io/courses/advanced-angular-forms)

In the `buildForm()` method, add the variable `formGroupFields` with the value from `getFormControlFields()` and assign the `formGroupFields` to the `registerForm`.

```typescript
 buildForm() {
    const formGroupFields = this.getFormControlsFields();
    this.registerForm = new FormGroup(formGroupFields);
  }
```

Next, render the fields in the `HTML`, using the `*ngFor` directive to iterate over the `fields` array. Use the variable `field` to show the label and set the input `formControlName` with the same field value.

```html
<form [formGroup]="registerForm">
    <div *ngFor="let field of fields">
           <label>{{field}}</label>
    <input type="text" [formControlName]="field"/>
</form>
```

Save changes, and we get the same form, generated dynamically, from the definition.

![2.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667830293116/eZKVxuDBm.png align="left")

But it's just the beginning. We want to split a little bit of the responsibility to allow us to make the form flexible to changes without pain.

## Separate the Form Process and FieldType

The `app.component` does a few tasks, creating the model, form, and rendering the input. Let's clean it up a little bit.

Create the `dynamic-form.component` with an input property to get the model to generate, and update `registerForm` to `dynamicFormGroup`. Move the function `buildForm` and `getFormControlsFields` to the dynamic form component.

```typescript
import { Component, Input, OnInit } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";

@Component({
  selector: "app-dynamic-form",
  templateUrl: "./dynamic-form.component.html",
  styleUrls: ["./dynamic-form.component.css"],
})
export class DynamicFormComponent implements OnInit {
  dynamicFormGroup: FormGroup;
  @Input() model: {};

  fields = [];

  ngOnInit() {
    this.buildForm();
  }

  buildForm() {
    const formGroupFields = this.getFormControlsFields();
    this.dynamicFormGroup = new FormGroup(formGroupFields);
  }

  getFormControlsFields() {
    const formGroupFields = {};
    for (const field of Object.keys(this.model)) {
      formGroupFields[field] = new FormControl("");
      this.fields.push(field);
    }
    return formGroupFields;
  }
}
```

Remember to update the `formGroup` in the html to `dynamicFormGroup`

Next, create a new component `dynamic-field` with the responsibility of rendering the field. Add two `Input()` properties `field` and `formName`.

```typescript
import {Component, Input} from "@angular/core";

@Component({
  selector: "app-field-input",
  templateUrl: "./dynamic-field.component.html",
  styleUrls: ["./dynamic-field.component.css"],
})
export class DynamicFieldComponent {
  @Input() field: {};
  @Input() formName: string;
}
```

Add the `HTML` markup with the input and the label.

```html
<form [formGroup]="formName">
    <label>{{field}}</label>
    <input type="text" [formControlName]="field"/>
</form>
```

Open the `app.component` to pass the model to the dynamic form. It takes the responsibility to process the model, and the `dynamic-field` renders the input.

```typescript
import { Component } from "@angular/core";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"],
})
export class AppComponent {
  model = {
    name: "",
    lastName: "",
    address: "",
    age: "",
  };
}
```

The `HTML` passes the property model with the definition.

```html
<app-dynamic-form [model]="model"></app-dynamic-form>
```

Perfect, we have a few separate tasks and responsibilities. The following challenge shows different control types.

## Show Inputs By Type

The Dynamic form renders a single type of input. In a real-world scenario, we need more types like `date`, `select`, `input`, `radio`, and `checkbox`.

The information about the control types must come from the model to the `dynamic-field`.

Change the `model` with the following properties `type`, `value`, and `label`. To make it a bit fun, change the age to type `number`, and create a new property `birthDay` of type `date`.

```typescript
model = {
    firstname: {
      type: "text",
      value: "",
      label: "FirstName",
    },
    lastname: {
      type: "text",
      value: "",
      label: "LastName",
    },
    address: {
      type: "text",
      value: "",
      label: "Address",
    },
    age: {
      type: "number",
      value: "",
      label: "age",
    },
    birthDay: {
      type: "date",
      value: "",
      label: "Birthday",
    },
  };
}
```

Save the new field `birthDay` shown in the form.

![3.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667830278165/IvJKw6KQF.png align="left")

We will make small changes in the `getFormsControlsFields` method to process the metadata.

Please create a new variable, `fieldProps` to store the field with the metadata from the model. Use the value property to assign the `formControl` and push the field with the property `fieldName` in the fields array.

*We will use the metadata properties in the dynamic-field component*

```typescript
getFormControlsFields() {
    const formGroupFields = {};
    for (const field of Object.keys(this.model)) {
      const fieldProps = this.model[field];
      formGroupFields[field] = new FormControl(fieldProps.value);
this.fields.push({ ...fieldProps, fieldName: field });
    }
    return formGroupFields;
  }
```

Finally, go to the `dynamic.component.html` and use these properties `field.label`, change `formControlName` to use `field.fieldName`, and bind the type with `field.type`.

```html
<form [formGroup]="formName">
    <label>{{field.label}}</label>
    <input [type]="field.type" [formControlName]="field.fieldName"/>
</form>
```

Save the changes and see the new controls with a type.

![4.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667830270052/cFdqowCbM.png align="left")

## Add Selects, Radios, and Checkbox

The dynamic field component shows the input, but adding controls like `select`, `radio`, or `checkbox` makes it a bit complex. I want to split each control into specific controls.

Create components for each control `dynamic-input` `dynamic-radio`, `dynamic-select`, and `dynamic-checkbox`.

```bash
ng g components/dynamic-field/dynamic-checkbox
ng g components/dynamic-field/dynamic-radio
ng g components/dynamic-field/dynamic-select
ng g components/dynamic-field/dynamic-input
```

Every component has two points in common, the field with metadata and the `FormGroup` to like with the main form.

Let's start with the Input and Checkbox:

### Input

Declare the `field` object with metadata and the `formName` as input properties.

```typescript
export class DynamicInputComponent {
  @Input() field: {};
  @Input() formName: FormGroup;
}
```

In the HTML Markup, use the metadata with the label and the `formControlName` with the `fieldName`.

```html
<form [formGroup]="formName">
    <label>{{field.label}}</label>
    <input [type]="field.type" [formControlName]="field.fieldName"/>
</form>
```

### Checkbox

Like the `dynamic-input component`, add two fields with the field metadata and the `formGroup`.

```typescript
export class DynamicCheckboxsComponent {
  @Input() field: any;
  @Input() formName: FormGroup;
}
```

In the HTML Markup, add a checkbox.

```typescript
<form [formGroup]="formName">
    <label>
        {{ field.label }}
        <input
                type="checkbox"
                [name]="field.fieldName"
                [formControlName]="field.fieldName"
                [value]="field.value"
        />
    </label>
</form>
```

I want to split the checkbox from the input for personal reasons; the checkbox sometimes has particular styles.

### Select

The properties are the same, but the metadata will become different. The select has a list of options, so we need to iterate over the list using the *ngFor* directive.

The HTML Markup looks like this:

```typescript
<form [formGroup]="formName">

    <label>{{field.label}}:</label>
    <select [formControlName]="field.fieldName">
        <option *ngFor="let option of field.options" [value]="option.value">
            {{option.label}}
        </option>
    </select>
</form>
```

## Radio

The radio is close, similar to the select with a list of `options`, but with a particular case, the name must be the same to allow select one single option. We add an extra `label` to show the option `label`.

```html
<form [formGroup]="formName">
    <h3>{{field.label}}</h3>
    <label *ngFor="let option of field.options">

        <label ngFor="let option of field.options">
            <input type="radio"
                   [name]="field.fieldName"
                   [formControlName]="field.fieldName"
                   [value]="option.value"
            >
            {{option.label}}
        </label>
    </label>
</form>
```

Ok, all components are ready, but with two missing points: show the components and update the metadata.

## Show Dynamic Components And Update Model

We have components for each control type, but the `dynamic-field.component` is a bridge between them.

It picks the specific component by type. Using the `ngSwitch` directive, we determine the control matching with the component type.

The final code looks like this:

```html
<ng-container [ngSwitch]="field.type">
    <app-dynamic-input *ngSwitchCase="'text'" [formName]="formName" [field]="field"></app-dynamic-input>
    <app-dynamic-select *ngSwitchCase="'select'" [formName]="formName" [field]="field"></app-dynamic-select>
    <app-dynamic-radio *ngSwitchCase="'radio'" [formName]="formName" [field]="field"></app-dynamic-radio>
    <app-dynamic-checkboxs *ngSwitchCase="'checkbox'" [formName]="formName" [field]="field"></app-dynamic-checkboxs>
</ng-container>
```

*Learn more about* [*switchCase*](https://angular.io/api/common/NgSwitchCase)

Next, we add new fields with the metadata for each type:

typeBussines: `radio` suscriptionType: `select` newsletterIn: `checkbox`

The type `radio` and `select` must have the options object with `{ label, value}` fit component expectations.

```typescript
   typeBussines: {
      label: "Bussines Type",
      value: "premium",
      type: "radio",
      options: [
        {
          label: "Enterprise",
          value: "1500",
        },
        {
          label: "Home",
          value: "6",
        },
        {
          label: "Personal",
          value: "1",
        },
      ],
    },
    newsletterIn: {
      label: "Suscribe to newsletter",
      value: "email",
      type: "checkbox"
    },
    suscriptionType: {
      label: "Suscription Type",
      value: "premium",
      type: "select",
      options: [
        {
          label: "Pick one",
          value: "",
        },
        {
          label: "Premium",
          value: "premium",
        },
        {
          label: "Basic",
          value: "basic",
        },
      ],
    },
```

Save and reload. The new components work with the structure and the `dynamic-field` picks the specific component.

![5.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1667830246450/vM4sXB8rn.png align="left")

## Validations

We need a complete form with validations. I want to make this article brief, but validation is essential in the forms.

*My example is basic about adding a required validator but feel free to add more if you want.*

First, we must change the `model` with new metadata `rules`, with the field `required` with the `true` value.

```typescript
firstname: {
      type: "text",
      value: "",
      label: "FirstName",
      rules: {
        required: true,
      }
    },
```

The validators are part of the form controls. We process the rule to set the validator for the `formControl` in a new method, `addValidators`, and the return value stored in the `validators` variable to assign in the `formControl`.

```typescript
      const validators = this.addValidator(fieldProps.rules);
      formGroupFields[field] = new FormControl(fieldProps.value, validators);
```

*If the rule object is empty, return an empty array*

In the `addValidator`, use the `Object.keys` and iterate over every property in the `rules` object. Use a `switch case` to math with the value and return the `Validator`.

*In Our scenario, the rule required returns the Validator.required.*

```typescript
  private addValidator(rules) {
    if (!rules) {
      return [];
    }

    const validators = Object.keys(rules).map((rule) => {
      switch (rule) {
        case "required":
          return Validators.required;
          //add more cases for the future.
      }
    });
    return validators;
  }
```

Ok, we already configure the `formControl` with the validator, but we need to show the label if the control is invalid. Create a new component, `dynamic-error` , with two input properties, `formGroup`, and `fieldName`.

```typescript
import { Component, Input } from "@angular/core";
import { FormGroup } from "@angular/forms";

@Component({
  selector: "app-dynamic-error",
  templateUrl: "./dynamic-error.component.html",
  styleUrls: ["./dynamic-error.component.css"],
})
export class DynamicErrorComponent {
  @Input() formName: FormGroup;
  @Input() fieldName: string;
}
```

We find the control by name using the form reference in the HTML. If it is `invalid`, `dirty`, or `touched` by the user, show a message.

```html
<div *ngIf="formName.controls[fieldName].invalid && (formName.controls[fieldName].dirty || formName.controls[fieldName].touched)"
     class="alert">
    <div *ngIf="formName.controls[fieldName].errors.required">
        * {{fieldName}}
    </div>
</div>
```

Finally, add the `dynamic-error` component in the `dynamic-form` component and pass the `fieldName` and the `formGroup`.

```html
<form [formGroup]="dynamicFormGroup">
    <div *ngFor="let field of fields">
        <app-field-input [formName]="dynamicFormGroup" [field]="field"></app-field-input>
        <app-dynamic-error [formName]="dynamicFormGroup" [fieldName]="field.fieldName"></app-dynamic-error>
    </div>
</form>
```

*Read more about* [*validators in Angular*](https://angular.io/guide/form-validation#validating-input-in-reactive-forms)

![dynamicforms.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1667845221682/QvZ-h7NxR.gif align="left")

Yeah!! The validators work with our dynamic forms.

You have a stable version of dynamic forms if you reach this part. I try to make this post short, but I hear feedback from other users like @[Juan Berzosa Tejero](@juanberzosa) and .... motivated to do some refactors.

## Refactor Time

### Propagation of FormGroup

After @[Juan Berzosa Tejero](@juanberzosa) take time to review the article, he asked me about the propagation of `FormGroup` using the `@Input()` with `formName`, and it starts to make noise. Luckily I found the directive `FormGroupDirective` in the Angular Documentation. It helps us to bind an existing `FormGroup` or `FormRecord` to a DOM element.

I decide to use and refactor the code; we are going to start with the `dynamic-error.component` to simplify, but the steps are similar for all child components.

Remove the `@Input()` decorator from `formName` and inject the `FormGroupDirective` in the component constructor.

Add the `ngOnInit` lifecycle to set the `formName` with the `FormGroupDirective.control` to bind the `FormGroup` to it.

The final code looks like this:

```typescript
import { Component, Input, OnInit } from "@angular/core";
import { FormGroup, FormGroupDirective } from "@angular/forms";

@Component({
  selector: "app-dynamic-error",
  templateUrl: "./dynamic-error.component.html",
  styleUrls: ["./dynamic-error.component.css"],
})
export class DynamicErrorComponent implements OnInit {
  formName: FormGroup;
  @Input() fieldName: string;

  constructor(private formgroupDirective: FormGroupDirective) {}

  ngOnInit() {
    this.formName = this.formgroupDirective.control;
  }
}
```

The `dynamic-form` doesn't need to pass the `formGroupName` anymore. It only needs the field metadata. The code looks like this:

```html
<form [formGroup]="dynamicFormGroup">
    <div *ngFor="let field of fields">
        <app-field-input [field]="field"></app-field-input>
        <app-dynamic-error [fieldName]="field.fieldName"></app-dynamic-error>
    </div>
</form>
```

If you replicate the same for all child components, `dynamic-field` no longer needs to set `formName`.

```html
<ng-container [ngSwitch]="field.type">
    <app-dynamic-input *ngSwitchCase="'text'" [field]="field"></app-dynamic-input>
    <app-dynamic-input *ngSwitchCase="'number'" [field]="field"></app-dynamic-input>
    <app-dynamic-select *ngSwitchCase="'select'" [field]="field"></app-dynamic-select>
    <app-dynamic-radio *ngSwitchCase="'radio'" [field]="field"></app-dynamic-radio>
    <app-dynamic-checkboxs *ngSwitchCase="'checkbox'" [field]="field"></app-dynamic-checkboxs>
</ng-container>
```

Done, we did the refactor! Feel free to read more about [FormGroup Directive](https://angular.io/api/forms/FormGroupDirective).

### Remove the ngSwitch

Yesterday, @[Maxime Lyakhov](@mLyakhov), leave a message about the ngSwich. He was right about the ngSwitch in the HTML; it is difficult to maintain.

My first idea is to load the specific component dynamically using `ViewChild` and `ViewContainerRef` and set the input variables with the `setInput()` method.

*Note: I update the project to angular 14 because the* [*API to load dynamic components*](https://angular.io/guide/dynamic-component-loader) *is easier.*

First, add a template variable to the `ng-container` to make a reference using the viewchild.

```typescript
<ng-container #dynamicInputContainer>

</ng-container>
```

Next, declare the viewchild pointing to the dynamicInput container. It works as a placeholder for our dynamic components.

```typescript
@ViewChild('dynamicInputContainer', { read: ViewContainerRef}) dynamicInputContainer!: ViewContainerRef;
```

Add a new array with all supported components with key and component.

```typescript
supportedDynamicComponents = [
    {
      name: 'text',
      component: DynamicInputComponent
    },
    {
      name: 'number',
      component: DynamicInputComponent
    },
    {
      name: 'select',
      component: DynamicSelectComponent
    },
    {
      name: 'radio',
      component: DynamicRadioComponent
    },
    {
      name: 'date',
      component: DynamicInputComponent
    },
    {
      name: 'checkbox',
      component: DynamicCheckboxsComponent
    }
  ]
```

*Note: A service can provide the supported component or external variables list, but I try to keep the article short.*

Create `getComponentByType` method to find the component in the suppertedDynamicComponents , if not exist, return DynamicInputComponent.

```typescript
getComponentByType(type: string): any {
    const componentDynamic = this.supportedDynamicComponents.find(c => c.name === type);
    return componentDynamic.component || DynamicInputComponent;
  }
```

Next, a new method `registerDynamicField()`. It takes the responsibility of creating an instance from the `getComponentType()` and setting the input field required by the components.

We do three steps:

1. Get the component by type using the field property and store in the `componentInstance` variable.
    
2. Using the createComponent pass the instance and get the dynamic component.
    
3. Pass the field to the input `field` using the `setInput()` method.
    

```typescript
private registerDynamicField() {

    const componentInstance = this.getComponentByType(this.field.type)
    const dynamicComponent = this.dynamicInputContainer.createComponent(componentInstance)
    dynamicComponent.setInput('field', this.field);
    this.cd.detectChanges();
  }
```

Because the input property field changes , we need to trigger the change detection to keep the component sync.

_Learn more about [ChangeDetection_](https://angular.io/api/core/ChangeDetectorRef)

The ViewChild is only available on the AfterviewInit lifecycle, implement the interface and call the method `registerDynamicField`.

```typescript
  ngAfterViewInit(): void {
    this.registerDynamicField();
  }
```

Save the changes, everything continues working as expected, and the ngSwitch is gone.

[*Learn more about viewchild*](https://angular.io/api/core/ViewChild)

## Trigger Event onChange or onBlur

@[Rakesh Prakash](@rakeshprakash) asked how to trigger events on changes or blur events attached to the input fields, and the first idea came to my head.

by default, the form updates the values on every keystroke, triggers the validator and the update values, and may not always be desirable.

Sometimes we want to have control over the moment value updates and validators, but Angular helps us with the `updateOn` in Angular Forms.

The `updateOn` set the update strategy of our form controls and which DOM event triggers updates.

The options for `updateOn` in the `FormControl` supported are **'change' | 'blur' | 'submit';**

* `change` it is the default when the input or element DOM changes.
    
* `blur`: when the user blurs the DOM element;
    
* `submit`: when the submit event is triggered on the parent form.
    

Configure the model; for example, we want to trigger the update when the user blurs the name, add a new property in the model 'triggerOn'

```typescript
firstname: {
      type: "text",
      value: "",
      label: "FirstName",
      triggerOn: 'blur'
      rules: {
        required: true,
      }
    },
```

set the property update in the FormControl creation, use the property or set a default value like 'change'.

First, create a subscription to the control, to validate that it only triggers blur, not every time the input change.

Open the dynamic-form.component.ts

```typescript

  ngOnInit() {
    this.buildForm();
    this.dynamicFormGroup.controls['firstname'].valueChanges.subscribe((v) => {
      console.log(v);
    });
  }
```

Update the getFormControlsFields methods and add the validator and the new property updateOn in a single object

```typescript
formGroupFields[field] = new FormControl(fieldProps.value, {
        updateOn: fieldProps.triggerOn || 'change',
        validators,
      });
```

The final code looks like this:

```typescript
 private getFormControlsFields() {
    const formGroupFields = {};
    for (const field of Object.keys(this.model)) {
      const fieldProps = this.model[field];
      const validators = this.addValidator(fieldProps.rules);

      formGroupFields[field] = new FormControl(fieldProps.value, {
        updateOn: fieldProps.triggerOn || 'change',
        validators,
      });
      this.fields.push({ ...fieldProps, fieldName: field });
    }

    return formGroupFields;
  }
```

Save the changes, and the firstName only triggers the blur event; if it is not defined, then use the change by default.

If you want to apply the same for all form controls, check out my [Trigger Validation in Angular Forms](https://www.danywalls.com/triggering-validation-in-angulars-reactive-forms) article.

* [Read more about UpdateOn](https://angular.io/api/forms/AbstractControl#updateOn)
    

### Read Values

@[Fabian](@Gf10) asked me how to read the values from the dynamic form. We should read a single value or all dynamics properties in the model.

For a single value, use the field name and the dynamicForm.get method.

```typescript
this.dynamicFormGroup.get('name').value
```

If we want to read all properties in the model, use the `Object.keys`. It returns an array with all keys in the model. Use these keys with the get method in the dynamicForm to read the control value.

The code looks like this:

```typescript
Object.keys(this. model).forEach((k) => { console.log(this.dynamicFormGroup.get(k).value) });
```

* Read more about [Object keys](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values)
    

If you want to read the values in JSON Format, use the JSON.stringify method:

```typescript
 onSubmit() {
    console.log(JSON.stringify(this.dynamicFormGroup.value));
  }
```

* Read more about [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
    

## Recap

We learned how to add dynamic fields from a structure and generate inputs like `select`, `checkbox`, `radio`, or `inputs` a few times. The model may be an API response from the backend.

If we want to add a new field, add it to the API response, and you feel free to add more types in the dynamic-field component.

We can improve the code by using interfaces for each component type, like `dropdown`, `checkbox`, or the form itself. Also, create helper functions to create most of the boilerplate code for dropdown.

* [Source code](https://github.com/danywalls/dynamic-forms)
    
* [Play in StackBliz](https://stackblitz.com/github/danywalls/dynamic-forms?file=README.md)
    

Maybe you know a better way to do it if you have an idea? Let’s hear it.