Back to all posts

Forms in Angular: Template-driven forms


Template-driven forms rely on directives in the template to create and handle forms. They are best suited for simple scenarios and are easier to set up, making them perfect for beginners.

Setting Up the Project

import { Component } from '@angular/core';
import { FormsModule, NgForm } from '@angular/forms';

@Component({
  selector: 'app-login',
  standalone: true,
  templateUrl: './login.component.html',
  styleUrl: './login.component.css',
  imports: [FormsModule]
})
export class LoginComponent {

  onSubmit(formData:NgForm){
    // console.log(formData)

    if(formData.form.valid) {
      return;
    }

    const enteredEmail = formData.form.value.email;
    const enteredPassword = formData.form.value.password;

    console.log(enteredEmail, enteredPassword)


  }

}
<form #form="ngForm" (ngSubmit)="onSubmit(form)">
  <h2>Login</h2>

  <div class="control-row">
    <div class="control no-margin">
      <label for="email">Email</label>
      <input id="email" type="email" name="email" ngModel required email/>
    </div>

    <div class="control no-margin">
      <label for="password">Password</label>
      <input id="password" type="password" name="password" ngModel required minlength="6"  />
    </div>

    <button class="button">Login</button>
  </div>
</form>

Advance usage:

// login.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent {
  loginData = {
    email: '',
    password: ''
  };

  onSubmit() {
    if (this.loginForm.valid) {
      console.log('Form submitted', this.loginData);
      // Here you would typically make an API call
    }
  }
}
<!-- login.component.html -->
<div class="login-container">
  <h2>Login</h2>
  
  <form #loginForm="ngForm" (ngSubmit)="onSubmit()">
    <!-- Email Field -->
    <div class="form-group">
      <label for="email">Email</label>
      <input
        type="email"
        id="email"
        name="email"
        [(ngModel)]="loginData.email"
        #email="ngModel"
        required
        email
        class="form-control"
      >
      <div *ngIf="email.invalid && (email.dirty || email.touched)" class="error-message">
        <div *ngIf="email.errors?.['required']">Email is required</div>
        <div *ngIf="email.errors?.['email']">Please enter a valid email</div>
      </div>
    </div>

    <!-- Password Field -->
    <div class="form-group">
      <label for="password">Password</label>
      <input
        type="password"
        id="password"
        name="password"
        [(ngModel)]="loginData.password"
        #password="ngModel"
        required
        minlength="6"
        class="form-control"
      >
      <div *ngIf="password.invalid && (password.dirty || password.touched)" class="error-message">
        <div *ngIf="password.errors?.['required']">Password is required</div>
        <div *ngIf="password.errors?.['minlength']">Password must be at least 6 characters</div>
      </div>
    </div>

    <button 
      type="submit" 
      [disabled]="loginForm.invalid"
      class="submit-button"
    >
      Login
    </button>
  </form>
</div>

Key Concepts Explained

1. Two-way Data Binding

The [(ngModel)] directive creates two-way data binding between the form inputs and the component’s properties. For example:

[(ngModel)]="loginData.email"

This synchronizes the input value with the loginData.email property.

2. Form Validation

Template-driven forms support both built-in and custom validators:

  • required: Makes the field mandatory
  • email: Validates email format
  • minlength: Sets minimum length requirement

3. Form State and CSS Classes

Angular automatically adds CSS classes to form controls based on their state:

  • ng-valid/ng-invalid: Indicates validation state
  • ng-pristine/ng-dirty: Shows if the value has changed
  • ng-touched/ng-untouched: Indicates if the field has been interacted with

4. Error Handling

We use template variables to access the NgModel instance and check for errors

#email="ngModel"