Skip to main content
Angular Fundamentals·Lesson 5 of 5

Forms — Template-Driven & Reactive

Angular has two form strategies. Template-driven forms keep logic in the template — great for simple forms. Reactive forms keep logic in the component class — better for complex validation, dynamic fields, and testing.

Template-Driven Forms

Requires FormsModule in your module. Use ngModel to bind inputs:

// app.module.ts
import { FormsModule } from '@angular/forms';
@NgModule({ imports: [FormsModule, ...] })
<form #loginForm="ngForm" (ngSubmit="onSubmit(loginForm)">
  <div>
    <label for="email">Email</label>
    <input
      id="email"
      name="email"
      type="email"
      [(ngModel)="model.email"
      required
      email
      #emailField="ngModel">
    <span *ngIf="emailField.invalid && emailField.touched">
      Enter a valid email address.
    </span>
  </div>

  <button type="submit" [disabled="loginForm.invalid">Login</button>
</form>
export class LoginComponent {
  model = { email: '', password: '' }

  onSubmit(form: NgForm) {
    if (form.valid) console.log(this.model)
  }
}

Reactive Forms

Requires ReactiveFormsModule. Define the form structure in the component class:

import { FormBuilder, FormGroup, Validators } from '@angular/forms';

export class RegisterComponent implements OnInit {
  form!: FormGroup

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.form = this.fb.group({
      name:     ['', [Validators.required, Validators.minLength(2)]],
      email:    ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(8)]],
    })
  }

  get name()     { return this.form.get('name')! }
  get email()    { return this.form.get('email')! }
  get password() { return this.form.get('password')! }

  onSubmit() {
    if (this.form.valid) console.log(this.form.value)
  }
}
<form [formGroup="form" (ngSubmit="onSubmit()">
  <input formControlName="name">
  <span *ngIf="name.invalid && name.touched">Name is required (min 2 chars).</span>

  <input type="email" formControlName="email">
  <span *ngIf="email.invalid && email.touched">Valid email required.</span>

  <input type="password" formControlName="password">
  <span *ngIf="password.invalid && password.touched">Password must be 8+ chars.</span>

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

Live Form Example

Here's a registration form with real-time validation — the kind Angular would generate:

Ctrl+Enter
HTML
CSS
JS
Preview

Built-in Validators

ValidatorWhat it checks
Validators.requiredField is not empty
Validators.emailValid email format
Validators.minLength(n)At least n characters
Validators.maxLength(n)At most n characters
Validators.pattern(regex)Matches a regex
Validators.min(n)Number ≥ n
Validators.max(n)Number ≤ n

You can also write custom validators — a function that returns null (valid) or an error object.