Angular Template Driven Forms
March 12, 2025 5:08 AM
Template-driven forms in Angular are a way to create and manage forms using Angular's directives in the template. This approach leverages Angular's two-way data binding and directives to handle form validation, state management, and submission directly in the HTML template.
Key Features of Template-Driven Forms
- Simplicity: Template-driven forms are easy to set up and use, making them ideal for simple forms.
- Directives: Use Angular directives like
ngForm
,ngModel
, andngSubmit
to manage forms. - Two-Way Data Binding: Automatically syncs form data between the template and the component.
- Validation: Built-in support for form validation using Angular's validation directives.
- All the data binding and validations are delcared at the level of template only but not the template component class.
So first we will start by importing the "FormsModule" from "@angular/forms" and will place this module in the import array of our component. Note: I am using standalone component here which is bydefault true for Angular with version 19.
With the help of this "FormsModule" Angular will able to parse the "forms" tag as it is using "<form>" tag selector to identity the form elements and it will automatically take the control of your template forms by applying some built-in directive implicitly even though we have not added it anything on our template. The name of directive is "ngForm" directive and it will be applied automatically on the form element. This is the co-ordinating form level directive that will receive all the values and all the validations status of each form control. One of the feature of this NG Form directive is that it products one export which points onto self and we can access this export by declaring a template reference variable #loginForm.
This "ngForm" directive automatically does not do much as it needs to know/identify what are the available form controls that we want to track.
In order to get the form control tracked by ngForm, we have to place ngModel directive. this is to help ngForm directive to identify(link) the available form controls inside the form. Please note "name" attribute should be there on the form control element so ngModel can identify each individual the form control.
<section class="signup row center">
<form class="login-form" #loginForm="ngForm" (ngSubmit)="login(loginForm.value)">
<h2>Login</h2>
<div>
<label for="email">Email</label>
<input name="email" class="form-control" type="email" id="email" required ngModel>
</div>
<button class="btn btn-primary" type="submit" [disabled]="!loginForm.valid">Log In</button>
<div>
{{loginform.value | json }}
</form>
</section>
- The
#loginForm="ngForm"
directive binds the form to an AngularFormGroup
instance and assigns it to theloginForm
template variable. - This allows Angular to manage the form's state and validation
import { Component } from'@angular/core';
import { FormsModule, NgForm } from'@angular/forms';
import { JsonPipe } from '@angular/common';
@Component({
selector: 'app-login',
standalone: true,
imports: [FormsModule, JsonPipe],
templateUrl: './login.component.html',
styleUrl: './login.component.scss',
})
export class LoginComponent {
login(form:NgForm) {
constformValues=form.value;
console.log(formValues, form.valid);
}
}
Now if you just add an auxiliary div in the template and output the values for loginForm using string interpolation with json pipe. You find that if you change the values for any of these form controls, the ngForm values on the screen will also change in the real time. That means ngModel directive in each of the controls is binding to the control and this registering event listeners for the important events such as the key up event in order to troack the value for the form field.
Now if you click on the login we will see an object will be logged on the console, ngModel tracks the value of each input control via event handlers and ngForm is going to access the ngModel applied on the each controls and from there build a complete form value object.
Now let's say we want to prove that it is the ngModel directive which is tracking the values for the form field, we can simply interact with ngModel directive by assigning the ngModel value exported by the ngModel directive to a template reference variable. Well this is the common Angular convention where export has the same name as the Directive.
<input name="email" class="form-control"type="email" id="email" required ngModel #email="ngModel">
{{ email.value }}
Note: We are using "required" attribute on the every form fields and if one of these field is invalid then the whole status of form will be marked as invalid.
Understanding Angular Forms CSS State Classes
Angular provides several CSS classes that are automatically added to form elements based on their state. These classes can be used to style form elements based on their validation status, user interaction, and other factors. Understanding these classes can help you create more visually appealing and user-friendly forms
CSS State Classes
.ng-valid
:- Applied to form elements when they are valid.
- Indicates that the input value meets all validation criteria.
.ng-invalid
:- Applied to form elements when they are invalid.
- Indicates that the input value does not meet the validation criteria.
.ng-pending
:- Applied to form elements when they are undergoing asynchronous validation.
- Indicates that the validation status is not yet determined.
.ng-pristine
:- Applied to form elements when they have not been modified by the user.
- Indicates that the input value is the initial value.
.ng-dirty
:- Applied to form elements when they have been modified by the user.
- Indicates that the input value has been changed from the initial value.
.ng-touched
:- Applied to form elements when they have been visited by the user.
- Indicates that the input element has lost focus.
.ng-untouched
:- Applied to form elements when they have not been visited by the user.
- Indicates that the input element has not yet lost focus.
<section class="signup row center">
<form class="login-form col-lg-6 col-md-6 col-sm-12" #loginForm="ngForm" (ngSubmit)="login(loginForm)">
<h2>Login</h2>
<div>
<label for="email">Email</label>
<input name="email" class="form-control" type="email" id="email" required ngModel #email="ngModel">
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
Please enter a valid email address.
</div>
</div>
<div>
<label for="password">Password</label>
<input name="password" class="form-control" type="password" id="password" required ngModel #password="ngModel">
<div *ngIf="password.invalid && (password.dirty || password.touched)" class="alert alert-danger">
Please enter a valid password.
</div>
</div>
<button class="btn btn-primary mt20" type="submit" [disabled]="!loginForm.valid">Log In</button>
</form>
</section>
Angular automatically adds CSS classes like .ng-valid
, .ng-invalid
, .ng-dirty
, .ng-pristine
, .ng-touched
, and .ng-untouched
to the input elements based on their state
- For example, you can change the border color of an input element when it is invalid:
input.ng-invalid.ng-touched {
border-color: red;
}
- For example, you can show an error message when the email input is invalid and has been touched:
<div *ngIf="email.invalid && (email.dirty || email.touched)" class="alert alert-danger">
Please enter a valid email address.
</div>
Comments