Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Angular Dictionary
  3. Template-Driven Forms

Template-Driven Forms

Since: Angular 14(2022)

Template-Driven Forms in Angular are a way to build forms by placing directives in the HTML template. By importing FormsModule and using the ngModel directive, you can achieve two-way data binding. Since more of the code is concentrated in the template, this approach is well-suited for quickly creating relatively simple forms.

Basic Syntax

<!-- After importing FormsModule, place ngForm and ngModel in the template -->
<form #myForm="ngForm" (ngSubmit)="onSubmit(myForm)">

  <!-- The combination of name attribute and [(ngModel)] is required -->
  <input type="text" name="username" [(ngModel)]="user.username" />

  <button type="submit">Submit</button>
</form>

Main Directives and Attributes

Directive / AttributeDescription
FormsModuleThe module that must be imported into @NgModule or a standalone component to use template-driven forms.
ngFormA directive automatically applied to <form> elements that manages the validation state and values of the entire form. Can be referenced with a template reference variable (#myForm="ngForm").
[(ngModel)]Two-way binds an input field to a component property. Must be used together with the name attribute.
ngModel (one-way)Can also be used one-way without binding, as [ngModel] (display only) or (ngModelChange) (change detection only).
ngModelGroupGroups multiple fields within a form, allowing validation and value retrieval to be done together.
required / minlength, etc.Specifying standard HTML validation attributes lets Angular automatically apply validation.

Sample Code

An example of a simple form for entering a name and email address. Validation error messages are also displayed within the template.

<!-- contact-form.component.html -->
<!-- A contact form using template-driven forms to collect name and email address -->

<form #contactForm="ngForm" (ngSubmit)="onSubmit(contactForm)">

  <div>
    <label for="name">Name</label>
    <!-- Use the name attribute, ngModel, and required as a set -->
    <input
      id="name"
      type="text"
      name="name"
      [(ngModel)]="formData.name"
      required
      minlength="2"
      #nameField="ngModel"
    />
    <!-- dirty: whether any input has occurred, invalid: whether validation failed -->
    <p *ngIf="nameField.dirty && nameField.invalid">
      <span *ngIf="nameField.errors?.['required']">Name is required.</span>
      <span *ngIf="nameField.errors?.['minlength']">Please enter at least 2 characters.</span>
    </p>
  </div>

  <div>
    <label for="email">Email address</label>
    <input
      id="email"
      type="email"
      name="email"
      [(ngModel)]="formData.email"
      required
      email
      #emailField="ngModel"
    />
    <p *ngIf="emailField.dirty && emailField.invalid">
      <span *ngIf="emailField.errors?.['required']">Email address is required.</span>
      <span *ngIf="emailField.errors?.['email']">Please enter a valid email address.</span>
    </p>
  </div>

  <!-- Only allow the submit button to be pressed when the entire form is valid -->
  <button type="submit" [disabled]="contactForm.invalid">Submit</button>

</form>
// contact-form.component.ts
// A contact component using template-driven forms.
// Importing FormsModule enables ngModel and ngForm.

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

interface ContactFormData {
  name: string;
  email: string;
}

@Component({
  selector: 'app-contact-form',
  templateUrl: './contact-form.component.html',
  imports: [FormsModule, CommonModule],
  standalone: true,
})
export class ContactFormComponent {
  formData: ContactFormData = {
    name: '',
    email: '',
  };

  onSubmit(form: NgForm): void {
    if (form.invalid) {
      return;
    }
    console.log('Submitted data:', this.formData);

    form.reset();
  }
}

Grouping Fields with ngModelGroup

An example of managing related fields like address together with ngModelGroup.

<!-- register-form.component.html -->
<!-- Uses ngModelGroup to group address fields -->

<form #registerForm="ngForm" (ngSubmit)="onSubmit(registerForm)">

  <input type="text" name="username" [(ngModel)]="user.username" required />

  <!-- Fields within the address group can have their validation state checked together -->
  <div ngModelGroup="address" #addressGroup="ngModelGroup">
    <input type="text" name="state"  [(ngModel)]="user.address.state"  required />
    <input type="text" name="city"   [(ngModel)]="user.address.city"   required />
  </div>

  <!-- Display an error message when the entire group is invalid -->
  <p *ngIf="addressGroup.invalid && addressGroup.dirty">Please fill in all address fields.</p>

  <button type="submit" [disabled]="registerForm.invalid">Register</button>

</form>

CSS Classes for Validation State

Angular automatically applies CSS classes to form elements based on validation state. You can use these to apply styles.

CSS ClassWhen AppliedDescription
ng-validWhen validation succeedsIndicates that the input value is in a valid state.
ng-invalidWhen validation failsIndicates that the input value is in an invalid state.
ng-pristineWhen the user has not interacted at allIndicates that the form is in its initial state.
ng-dirtyWhen the user has changed a valueIndicates that input or changes have occurred at least once.
ng-touchedWhen focus has been placed and then removedIndicates that the field has been focused at least once.
ng-untouchedWhen focus has not yet been placedIndicates that the field has not yet been touched.
<!-- Example of styling based on validation state with CSS -->
<style>
  /* Show a red border for inputs that are dirty and invalid */
  input.ng-dirty.ng-invalid {
    border: 2px solid #e74c3c;
  }
  /* Show a green border for inputs that are dirty and valid */
  input.ng-dirty.ng-valid {
    border: 2px solid #2ecc71;
  }
</style>

Important Notes

ItemDescription
Required name attributeAlways specify the name attribute when using [(ngModel)]. Without it, Angular cannot register the form control and an error occurs.
Complex formsFor complex forms with dynamic fields or fine-grained programmatic control, reactive forms may be considered.
Change detection timingngModel relies on Zone.js change detection by default. Verify behavior when combining with the OnPush strategy.
Forgetting FormsModuleForgetting to import FormsModule causes ngModel to be treated as an unknown directive, resulting in a template compilation error.
Difficulty with unit testingSince logic is scattered across the template, unit testing can be more difficult compared to reactive forms.

Overview

Template-Driven Forms are a way to build forms easily in Angular. Simply import FormsModule and place ngModel to activate two-way binding and validation. Since standard HTML attributes like required and minlength can be used directly as validation rules, they can be written intuitively.

On the other hand, as forms become more complex, the template becomes larger and programmatic state control becomes more difficult. Consider choosing between this and Reactive Forms based on your form's requirements. For details on two-way binding, see ngModel.

If you find any errors or copyright issues, please .