How To Add Dynamic Controls With Reactive Forms Validations In Angular

Introduction

In this article, we are going to see how to add controls in angular applications dynamically like textbox, dropdown, radio button or date picker, etc. And followed the form validations in angular.

First, we need to set up an angular application and follow the steps below.

Add code to app.module.ts codes. In this code, we imported FormsModule & ReactiveFormsModule.

import {
    BrowserModule
} from '@angular/platform-browser';
import {
    NgModule
} from '@angular/core';
import {
    AppRoutingModule
} from './app-routing.module';
import {
    AppComponent
} from './app.component';
import {
    FormsModule,
    ReactiveFormsModule
} from '@angular/forms';
@NgModule({
    declarations: [AppComponent],
    imports: [BrowserModule, AppRoutingModule, FormsModule, ReactiveFormsModule],
    providers: [],
    bootstrap: [AppComponent]
})
export class AppModule {}

Next, we are going to add control selection, type, and values, placeholder, and label whatever we need. The selection control is dropdown is the control type. Please refer to the screenshots below. 

Here I have created in app.interface.ts file. We’re taking properties of the control in IFormField and IDropdown. And then declared Form group and IFormFields from app.component.ts file. 

app.interface.ts 

export interface IFormField {
    label: string;
    f_Name: string;
    f_Type: string;
    f_Value: string;
    placeholder: string;
    values: IDropdown[]; // To fill dropdown values 
}
export interface IDropdown {
    displayValue: string;
    internalValue: string;
}
export interface IUser {
    userName: string;
}

Here create the service file app.service.ts file in our root directory. And set Dropdown field value. 

import {
    Injectable
} from '@angular/core';
import {
    IDropdown
} from './app.interface';
@Injectable({
    providedIn: 'root',
})
export class AppService {
    getList() {
        let dropDown: IDropdown[] = [];
        let _Test_1 = < IDropdown > {
            displayValue: 'Test 1',
            internalValue: 'T1',
        };
        dropDown.push(_Test_1);
        let _Test_2 = < IDropdown > {
            displayValue: 'Test 2',
            internalValue: 'T2',
        };
        dropDown.push(_Test_2);
        return dropDown;
    }
}

In app.component.ts file, controls are added dynamically. We can also set it in UI form. We apply the different validation types with different controls(textbox,dropdown,radio,date picker...etc...). 

Please follow the app.component.ts file for

  • Adding controls to onSubmitControl function , 
  • Validate all types of controls to formValidation function, in email or password field we can set pattern validations as well, here I have added for email pattern validations. 

App.component.ts

import {
    Component,
    OnInit
} from '@angular/core';
import {
    FormBuilder,
    FormControl,
    FormGroup,
    Validators,
} from '@angular/forms';
import {
    IFormField,
    IDropdown,
    IUser
} from './app.interface';
import {
    AppService
} from './app.service';
@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
})
export class AppComponent {
    title = 'sample-form';
    sampleform: FormGroup;
    controlform: FormGroup;
    lstForm: IFormField[] = [];
    outputStr: string = '';
    typename: any;
    ValueType: any;
    constructor(private formBuilder: FormBuilder, private appService: AppService) {}
    ngOnInit(): void {
        this.sampleform = this.formBuilder.group({});
        this.controlform = this.formBuilder.group({
            typename: ['', Validators.required],
            valueType: ['0', Validators.required],
            labelname: ['', Validators.required],
        });
    }
    onSubmitControl() {
        if (this.controlform.value.valueType == "text") {
            let _un = < IFormField > {
                label: this.controlform.value.labelname,
                f_Name: this.controlform.value.typename,
                f_Type: this.controlform.value.valueType,
                f_Value: this.controlform.value.typename,
            };
            this.lstForm.push(_un)
        }
        if (this.controlform.value.valueType == "dropdown") {
            let stateList = this.appService.getList(); // Get test list from service 
            let _ddlStateList = < IFormField > {
                label: this.controlform.value.labelname,
                f_Name: this.controlform.value.typename,
                f_Type: this.controlform.value.valueType == "dropdown" ? 'select' : '',
                f_Value: '0',
                values: stateList,
            };
            this.lstForm.push(_ddlStateList);
        }
        if (this.controlform.value.valueType == "radio") {
            let _radio = < IFormField > {
                label: this.controlform.value.labelname,
                f_Name: this.controlform.value.typename,
                f_Type: this.controlform.value.valueType,
                f_Value: this.controlform.value.typename,
            };
            this.lstForm.push(_radio);
        }
        if (this.controlform.value.valueType == "datepicker") {
            let _radio = < IFormField > {
                label: this.controlform.value.labelname,
                f_Name: this.controlform.value.typename,
                f_Type: this.controlform.value.valueType == "datepicker" ? 'date' : '',
                f_Value: this.controlform.value.typename,
            };
            this.lstForm.push(_radio);
        }
        this.formValidation();
    }
    formValidation() {
        const group: any = {};
        for (var field of this.lstForm) {
            if (field.f_Type == 'text') {
                group[field.f_Name] = new FormControl(field.f_Value || '', [
                    Validators.required,
                ]);
            } else if (field.f_Name.toLowerCase().indexOf('email') > -1) {
                group[field.f_Name] = new FormControl(field.f_Value || '', [
                    Validators.required,
                    Validators.pattern('[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,6}$'),
                ]);
            } else if (field.f_Type == 'select') {
                group[field.f_Name] = new FormControl(field.f_Value || '', Validators.required);
            } else if (field.f_Type == 'radio') {
                group[field.f_Name] = new FormControl(false, null);
            } else if (field.f_Type == 'date') {
                group[field.f_Name] = new FormControl(field.f_Value || '', [
                    Validators.required,
                ]);
            }
        }
        this.sampleform = new FormGroup(group);
    }
    onSubmit() {
        this.formValidation();
        if (this.sampleform.valid) {
            console.log(this.lstForm);
        }
    }
}

App.component.html 

<div class="container">
  <div class="panel panel-default" style="width: 50%;">
    <div class="panel-heading">
      <h2>Add Controls(Text box, Dropdown, Date Picker, Radio button....etc)</h2>
    </div>
    <div class="panel-body">
      <form [formGroup]="controlform" (ngSubmit)="onSubmitControl()">
        <div class="row">
          <div class="col-md-3">
            <div class="form-group">
              <select  class="form-control" formControlName="valueType">
                <option value="0">Select Controls</option>
                <option value="text">Text Box</option>
                <option value="dropdown">Dropdown</option>
                <option value="datepicker">Date Pciker</option>
                <option value="radio">Radio Box</option>
              </select>
            </div>
          </div>
          <div class="col-md-3">
            <div class="form-group">
              <input type="text" name="typename" class="form-control" formControlName="typename" placeholder="Control Type">
            </div>
          </div>
          <div class="col-md-3">
            <div class="form-group">
              <input type="text" name="labelname" class="form-control" formControlName="labelname" placeholder="Label Name">
            </div>
          </div>
          <div class="col-md-3">
            <div class="form-group">
              <button type="submit" class="btn btn-primary">Add Control</button>
            </div>
          </div>
        </div>
      </form>
    </div>
  </div>
</div>
<div class="container">
  <div class="panel panel-default" style="width: 50%;">
    <div class="panel-heading">
      <h2>Sample Dynamic Form</h2>
    </div>
    <div class="panel-body">
      <form [formGroup]="sampleform" (ngSubmit)="onSubmit()">
        <div class="row">
          <div class="col-md-8" *ngFor="let formData of lstForm" [ngSwitch]="true">
            <div class="form-group">
              <label for="{{formData.f_Name}}">{{formData.label}}</label>
              <input type="{{formData.f_Type}}" name="{{formData.f_Name}}" class="form-control" *ngSwitchDefault [formControlName]="formData.f_Name" [(ngModel)]="formData.f_Value"                  placeholder="{{formData.placeholder}}">
              <select [id]="formData.f_Name" [formControlName]="formData.f_Name" *ngSwitchCase="formData.f_Type === 'select'" class="form-control" [(ngModel)]="formData.f_Value">
                <option [value]='0'>Select State</option>
                <option *ngFor="let opt of formData.values" [value]="opt.internalValue">{{opt.displayValue}}</option>
              </select>
              <div class="custom-control custom-switch" *ngSwitchCase="formData.f_Type === 'radio'">
                <input id="customSwitch1" type="checkbox" [checked]="formData.f_Value == 'Y'? true:false" class="custom-control-input">
                <label class="custom-control-label" for="customSwitch1">Yes</label>
              </div>
              <div *ngSwitchCase="formData.f_Type === 'select'">
                <div  class="error-txt-msg mb-2" *ngIf="sampleform.controls[formData.f_Name].value == '0'" style="color: red;">Required field.</div>
              </div>
              <div class="error-txt-msg mb-2" *ngIf="sampleform.controls[formData.f_Name].hasError('required')" style="color: red;">Required field.</div>
              <div class="error-txt-msg mb-2" *ngIf="sampleform.controls[formData.f_Name].hasError('pattern')" style="color: red;">Incorrect format.</div>
            </div>
          </div>
          <div class="form-group">
            <button type="submit" class="btn btn-primary">Submit</button>
          </div>
        </div>
      </form>
    </div>
  </div>
</div>     

Once all the above code is added please run the application using ng serve --open or npm start command. After the run, just clicks to add controls. In the text field, I have added Firstname, Lastname, and email. 

The dropdown list just adds and shows the test list already added from app.service.ts file. Please refer onSubmitControl and type dropdown codes in app.component.ts file. 

Here added dropdown, datepicker, and radio button. Please refer to the below images. 

How to add dynamic controls with Reactive Forms validations in angular

How to add dynamic controls with Reactive Forms validations in angular

How to add dynamic controls with Reactive Forms validations in angular

Added all types of controls in the form dynamically. If we need to validate this form while adding controls set default f_Value null or empty. Or else remove any one values from the sample dynamic form and validate the required field error. Please check the screenshot below for required field validations. 

How to add dynamic controls with Reactive Forms validations in angular

I hope this article is helpful for you to add dynamic controls with Reactive Forms validations in angular. 


Similar Articles