- September 14, 2017
- Posted by: user
- Category: Uncategorized
Learn using Angular how to build a simple dynamic form that can be easily extended based on the requirement. We will create dynamic form component that will allow the user to provide configuration object and will render the form according to provided object and will expose the form submit event.
In Angular, there are two ways to build forms one is Template-driven and another is Model-driven.
Angular allows dynamic rendering capabilities. We can build a dynamic form and configuration driven form component using dynamic rendering capabilities, Let’s see further now…
Introduction
We will build a dynamic form component that will accept the form configuration object and will expose the form submit event.
We’ll capture the form configuration based on below interface:
Error when loading gists from https://gist.github.com/. // dynamic-control.config.ts
export interface DynamicControlConfig {
type: string;
name: string;
label: string;
placeholder: string;
}
We have considered only basic form controls like text, password, and date in this example. type
represents type of the input, name
represents name of the form control, label
represents label of the input, and so on.
Requirements
- Create a form based on the form configuration object
- Listen on form submit event and get the form data
Application Setup & Implementation
Here is the file structure:
|– dynamic-form
|– dynamic-form-control
|– dynamic-form-control.component.ts
|– dynamic-control.config.ts
|– dynamic-form.component.ts
|– dynamic-form.module.ts
|– app.component.html
|– app.component.ts
|– app.module.ts
To use angular forms, we will have to import FormsModule and ReactiveFormsModule from the package @angular/forms
into our DynamicFormModule
.
Here is the DynamicFormModule:
Error when loading gists from https://gist.github.com/. // dynamic-form.module.ts
import { DynamicFormComponent } from ‘./dynamic-form.component’;
import { ReactiveFormsModule, FormsModule } from ‘@angular/forms’;
import { DynamicFormControlComponent } from ‘./dynamic-form-control/dynamic-form-control.component’;
import { NgModule } from ‘@angular/core’;
import { CommonModule } from ‘@angular/common’;
@NgModule({
imports: [
CommonModule,
ReactiveFormsModule,
FormsModule
],
exports: [DynamicFormControlComponent, DynamicFormComponent],
declarations: [DynamicFormControlComponent, DynamicFormComponent],
entryComponents: [DynamicFormControlComponent, DynamicFormComponent]
})
export class DynamicFormModule { }
Now that we have created DynamicFormModule
, we need to create one component that will take configuration object as an input and will create a form control using that configuration object, Here is the component for that:
Error when loading gists from https://gist.github.com/. // dynamic-form-control.component.ts
import { DynamicControlConfig } from ‘./../dynamic-control.config’;
import { FormGroup } from ‘@angular/forms’;
import { Component } from ‘@angular/core’;
@Component({
// tslint:disable-next-line:component-selector
selector: ‘dynamic-form-control’,
template:
<div [formGroup]="formGroup" class="form-group">
<label>{{controlConfig.label}}</label>
<input [type]="controlConfig.type" class="form-control"
[placeholder]="controlConfig.placeholder" [formControlName]="controlConfig.name">
</div>
})
export class DynamicFormControlComponent {
formGroup: FormGroup;
controlConfig: DynamicControlConfig;
constructor() { }
}
DynamicFormControlComponent
will not be used externally, we will be using it internally to add the form control
dynamically to the form. To add the form control dynamically to the form we will need the dynamic form component
that will accept the configuration object from the user and will create the form accordingly. Here is the code of DynamicFormComponent
:
Error when loading gists from https://gist.github.com/. // dynamic-form.component.ts
import { DynamicFormControlComponent } from ‘./dynamic-form-control/dynamic-form-control.component’;
import { DynamicControlConfig } from ‘./dynamic-control.config’;
import { FormGroup, FormControl } from ‘@angular/forms’;
import { Component, OnInit, Input, EventEmitter, Output, ViewChild } from ‘@angular/core’;
import { ViewContainerRef, ComponentFactoryResolver, OnChanges } from ‘@angular/core’;
@Component({
// tslint:disable-next-line:component-selector
selector: ‘dynamic-form’,
template:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<ng-container #container></ng-container>
<button type="submit" class="btn btn-outline-primary">submit
</button>
</form>
})
export class DynamicFormComponent implements OnChanges {
_formConfig: DynamicControlConfig[];
private cfr: ComponentFactoryResolver;
public form: FormGroup;
@Input()
set formConfig(value: any) {
this._formConfig = value;
}
@Output() formSubmit: EventEmitter<any>;
@ViewChild(‘container’, { read: ViewContainerRef })
formContainer: ViewContainerRef;
onSubmit() {
this.formSubmit.emit(this.form.value);
}
constructor(cfr: ComponentFactoryResolver) {
this.cfr = cfr;
this.form = new FormGroup({});
this.formSubmit = new EventEmitter<any>();
}
ngOnChanges() {
if (this._formConfig) {
this.formContainer.clear();
this._formConfig.forEach(controlConfig => {
this.form.addControl(controlConfig.name, new FormControl());
this.buildControl(controlConfig);
});
}
}
private buildControl(controlConfig: DynamicControlConfig): void {
const factory = this.cfr.resolveComponentFactory(DynamicFormControlComponent);
const control = this.formContainer.createComponent(factory);
control.instance.controlConfig = controlConfig;
control.instance.formGroup = this.form;
}
}
- We get an array of
DynamicControlConfig
types through@Input
binding. - We query the <ng-container> component as a
ViewContainerRef
. We will use this to attach our dynamically created control components to the template. - Inside the constructor, we create an empty
FormGroup
instance. - We implement
ngOnChanges()
with simple loop that goes through theDynamicControlConfig (_formConfig)
array, adding controls to theFormGroup
, and call thebuildControl
method. - The
buildControl()
method is responsible for instantiating theDynamicControlComponent
and initializing its properties withcontrolConfig
object and theFormGroup
instance. - We catch the
ngSubmit
event and emit a custom event namedformSubmit
with form data.
Now our component is ready to use, all we need to do is to import the DynamicFormModule into our AppModule, here is our AppModule:
Error when loading gists from https://gist.github.com/. // app.module.ts
import { DynamicFormModule } from ‘./dynamic-form’;
import { BrowserModule } from ‘@angular/platform-browser’;
import { NgModule } from ‘@angular/core’;
import { AppComponent } from ‘./app.component’;
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
DynamicFormModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
We will use our dynamic form component inside AppComponent, here is AppComponent:
Error when loading gists from https://gist.github.com/.
import { DynamicControlConfig } from ‘./dynamic-form/dynamic-control.config’;
import { Component } from ‘@angular/core’;
@Component({
selector: ‘app-root’,
templateUrl: ‘./app.component.html’,
styleUrls: [‘./app.component.css’]
})
export class AppComponent {
formConfig: DynamicControlConfig[];
constructor() {
this.formConfig = [
{
type: ‘text’,
name: ‘username’,
label: ‘Username’,
placeholder: ‘Type username’,
}, {
type: ‘password’,
name: ‘password’,
label: ‘Password’,
placeholder: ‘Type password’,
}
];
}
onSubmit(event: any) {
console.log(‘form submitted’, event);
}
}
Here is the markup of AppComponent:
Error when loading gists from https://gist.github.com/. <div class="container">
<div class="col-md-6">
<h3 class="title">Dynamic Form Component</h3>
<dynamic-form [formConfig]="formConfig" (formSubmit)="onSubmit($event)"></dynamic-form>
</div>
</div>
The output should looks a like below, I’ve used Bootstrap for styling.
When you hit the submit button it’ll log the form data in browser console.
Summary
That’s it, So it is easy to create a simple configuration-driven form, it can be extended to any level based on the requirements. You can add validators to the dynamic component, you can provide custom CSS classes to customize UI of the form, and many other things can be passed in the configuration object.
Happy coding, stay awesome!