Almost all enterprise applications are heavily form-driven. Some apps have massive forms that span multiple steps and dialogs and integrate complex validation logic. If developers were to build these complex forms from scratch, they would have to:
These challenges show that building forms for enterprise apps from scratch is inefficient, time-consuming, and error-prone.
Angular provides two modules for creating and managing forms for complex apps. This article will explore the two modules readily available, provide in-depth comparisons to determine when to use each module, and finally look at a demonstration on managing forms using the two modules.
A template-driven form is the simplest way to build a form in Angular. It uses Angular’s two-way data-binding directive (ngModel) to create and manage the underlying form instance.
Additionally, as the name suggests, a template form is mainly driven by the view component. So, it uses directives placed in HTML rather than TypeScript or JavaScript to manage the form. A template-driven form is asynchronous due to the use of “directives” because the creation of form controls is delegated to the declared directives (IoC).
Using template-driven forms has several advantages:
However, using template-driven forms has minor drawbacks, too. A few common disadvantages are:
It is essential to know when to use a template-driven form in your Angular app. You may use a template-driven form if:
This example assumes you have already created a new Angular project with Bootstrap for CSS.
To use template-driven forms, Angular requires the FormsModule to be imported into your AppModule (app.module.ts). You can see the import in the following code example.
import { NgModule } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, // import the FormsModule to enable Template Driven Forms FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
This import will create the directives required for template-driven forms to be available throughout the app.
Afterward, declare a form in any view template. I am creating a component (ng g c TemplateDriven ) to implement the form for demonstration purposes.
In the new component, create a sample form that contains three fields: Name, Email, and Age. The following is the code for the form and its expected output.
<div class="row justify-content-sm-center"> <div class="col-sm-6"> <form> <div class="form-group"> <label>Name</label> <input class="form-control" placeholder="Provide your name" /> </div> <div class="form-group"> <label>Email Address</label> <input class="form-control" placeholder="Provide your email address" /> </div> <div class="form-group"> <label>Age</label> <input class="form-control" placeholder="Provide your age" /> </div> <div class="form-group"> <div style="display: flex; margin-top: 10px;"> <button type="submit" class="btn btn-primary" style="margin-left: auto; width: 30%;" > Submit </button> </div> </div> </form></div> </div>
As shown in the previous code snippet, the fields get wrapped in the <form> </form> tags. With the help of the FormsModule, bind Angular-specific directives to the form to create the template-driven form.
Then, define the structure of the form by declaring its controls. Ensure that the form outputs an object containing the keys as the field name and the value entered.
Place the directive ngModel (provided by FormsModule ) on the input field that you want to mark as a control. Add the HTML name attribute to define the the key that the form uses for the data model. Refer to the following code.
<!-- placing the ngModel directive and the NAME attribute (for data structure definition) on the form controls initially declared --> <input class="form-control" placeholder="Provide your name" ngModel name="name" /> <input class="form-control" placeholder="Provide your email address" ngModel name="emailAddress"/> <input class="form-control" placeholder="Provide your age" ngModel name="age"/>
By doing so, Angular will register key-value pairs and create the JavaScript representation of the form (data model).
Make sure that your input fields get adequately validated on the front end. Template-driven forms allow you to add validations easily by binding the native HTML validators to the input fields.
For our form, let’s make all the fields as required and add property input types as shown in the following code.
<!-- Adding the HTML Validators to the Form Controls --> <input class="form-control" placeholder="Provide your name" ngModel name="name" required type="text" /> <input class="form-control" placeholder="Provide your email address" ngModel name="emailAddress" required type="email"/> <input class="form-control" placeholder="Provide your age" ngModel name="age" required type="number"/>
To submit the form, we require two things:
In Angular, the ngSubmit directive declares the submit event for the form. Therefore, when the Submit button is clicked, the event handler for ngSubmit() is executed. The callback for the event is declared in the component class. For the event, we have to pass the form values. To do this, create a local reference to the form of type ngForm. Refer to the following code.
<form #templateDrivenForm="ngForm" (ngSubmit)="handleFormSubmit(templateDrivenForm)"> </form>
import { Component, OnInit } from '@angular/core'; import { NgForm } from '@angular/forms'; @Component({ selector: 'app-template-driven', templateUrl: './template-driven.component.html', styleUrls: ['./template-driven.component.css'] }) export class TemplateDrivenComponent implements OnInit { constructor() { } ngOnInit(): void { } handleFormSubmit(form: NgForm): void { // value will print the JavaScript Object of the Form Values. console.log(form.value); } }
Once we submit the form, you can see the form values logged in the console.
To add validation messages, use the local reference, check for individual control errors, and disable the submit action if the form has an error present.
The output shows the final form template and validation messages.
<div class="row justify-content-sm-center"><div class="col-sm-6"><form #templateDrivenForm="ngForm" (ngSubmit)="handleFormSubmit(templateDrivenForm)"><div class="form-group"><label>Name</label><input class="form-control" placeholder="Provide your name" ngModel name="name" required type="text" /><span class="text-danger" *ngIf="templateDrivenForm.controls['name']?.invalid && templateDrivenForm.controls['name'].dirty"> Name is required </span></div> <div class="form-group"><label>Email Address</label><input class="form-control" placeholder="Provide your email address" ngModel name="emailAddress" required type="email"/><span class="text-danger" *ngIf="templateDrivenForm.controls['emailAddress']?.invalid && templateDrivenForm.controls['emailAddress'].dirty"> Email is required </span></div> <div class="form-group"><label>Age</label><input class="form-control" placeholder="Provide your age" ngModel name="age" required type="number"/><span class="text-danger" *ngIf="templateDrivenForm.controls['age']?.invalid && templateDrivenForm.controls['age'].dirty"> Age is required </span></div> <div class="form-group"><div style="display: flex; margin-top: 10px;"><buttontype="submit"class="btn btn-primary"style="margin-left: auto; width: 30%;" [disabled]="templateDrivenForm.invalid" Submit </button></div></div></form></div> </div>
The second way to develop forms in Angular is to use reactive forms. Reactive forms utilize the component class to programmatically declare the form controls and the required validators synchronously.
We then bind the controls to the input fields in the HTML template. Reactive forms use classes such as FormGroup and FormControl to define groups and controls wired to the template.
Reactive forms advantages:
Reactive forms drawbacks:
If the learning time and added use of the API are not a bother to you, you should consider using a reactive form in these cases:
If you want to incorporate reactive forms in your app, let’s look at an example to help you get started. This example assumes you already created a new Angular project with Bootstrap for CSS.
Angular requires the ReactiveFormsModule to enable reactive forms for an Angular app. We can add it to the AppModule (app.module.ts) by importing the ReactiveFormsModule.
import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; @NgModule({ declarations: [ AppComponent, ], imports: [ BrowserModule, // Import the ReactFormsModule to use Reactive Forms in the application. ReactiveFormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
This import ensures that the classes required to construct reactive forms are available throughout the app.
The controls are defined in the Component class in a reactive form, while the layout is defined in the template. Let’s convert the template-driven form we created earlier to a reactive form. To do so, we use a new component (ng g c ReactiveForms). The Reactive Forms Module offers the FormGroup and FormControl classes to build the form. Additionally, it provides built-in validator functions to test for required fields, regex patterns, and more.
You can see the sample form structure with validators and the submit callback in the following code.
import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-reactive-form', templateUrl: './reactive-form.component.html', styleUrls: ['./reactive-form.component.css'] }) export class ReactiveFormComponent implements OnInit { sampleForm!: FormGroup; ngOnInit(): void { // create an instance of form group// for the object passed, the key identifies the control name, the value identifies the actual control// the Validators provides validator functions that need to be applied to the control value this.sampleForm = new FormGroup({'name': new FormControl('', [Validators.required]), 'emailAddress': new FormControl('', [Validators.required, Validators.email]), 'age': new FormControl('', [Validators.required, Validators.pattern('^[0-9]+
After configuring the structure, validators, and submit events, define the template for the form and use the directives ngForm and formControlName to bind the declared form for the view. Refer to the following code.
<div class="row justify-content-sm-center"><div class="col-sm-6"><form [formGroup]="sampleForm" (ngSubmit)="handleSubmit()"><div class="form-group"><label>Name</label><input class="form-control" placeholder="Provide your name" type="text" formControlName="name"/><span class="text-danger" *ngIf="sampleForm.get('name')?.invalid && sampleForm.get('name')?.touched"> Name is invalid </span></div><div class="form-group"><label>Email Address</label><input class="form-control" placeholder="Provide your email address" required type="email" formControlName="emailAddress"/><span class="text-danger" *ngIf="sampleForm.get('emailAddress')?.invalid && sampleForm.get('emailAddress')?.touched"> Email is invalid </span></div><div class="form-group"><label>Age</label><input class="form-control" placeholder="Provide your age" type="number" formControlName="age"/><span class="text-danger" *ngIf="sampleForm.get('age')?.invalid && sampleForm.get('age')?.touched"> Age is invalid </span></div><div class="form-group"><div style="display: flex; margin-top: 10px;"><buttontype="submit"class="btn btn-primary"style="margin-left: auto; width: 30%;" [disabled]="sampleForm.invalid" > Submit </button></div></div></form></div> </div>
This code binds the initialized form as the form group on the <form> tag and binds the controls by the formControlName directive. The value placed here is the value declared during initialization.
After adding the code, the reactive form should function the same as before, but this time, with cleaner and scalable code.
The code used in this article is accessible in this GitHub repository.
Angular takes away the added complexity of building forms by providing two modules: template-driven and reactive forms. There is no best way to create a form. You can use either of the two modules based on your use case.
I recommend using a template-driven form to build simple forms with minimal validation. But if you require granular control of input fields with asynchronous and complex validators, reactive forms are the ones for you.
I hope you found this article helpful.
Thank you for reading.
Syncfusion’s Angular UI component library is the only suite you will ever need to build an app. It contains over 65 high-performance, lightweight, modular, and responsive UI components in a single package.
For existing customers, the newest Essential Studio® version is available for download from the License and Downloads page. If you are not yet a Syncfusion customer, you can try our 30-day free trial to check out the available features. Also, check out our demos on GitHub.
For questions. you can contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!