Forms

Angular has two ways to build forms: template-driven, where the template does most of the work via directives, and reactive, where the form's structure lives in the component class. This lesson covers template-driven, the more approachable of the two.

Binding an input with ngModel

TS signup.component.ts
export class SignupComponent {
  email = '';

  onSubmit() {
    console.log('Submitted email:', this.email);
  }
}
HTML signup.component.html
<form (ngSubmit)="onSubmit()">
  <input name="email" [(ngModel)]="email" type="email">
  <button type="submit">Sign up</button>
</form>
Console output after typing "sam@example.com" and submitting
Submitted email: sam@example.com

[(ngModel)]="email" keeps the input and the component's email field in sync in both directions. (ngSubmit) fires when the form is submitted and, unlike a plain (submit) event, already has the browser's default full-page-reload-on-submit behavior handled for you.

Tracking validity

HTML signup.component.html — with validation
<form #signupForm="ngForm" (ngSubmit)="onSubmit()">
  <input name="email" [(ngModel)]="email" type="email" required>
  <button type="submit" [disabled]="signupForm.invalid">Sign up</button>
</form>

#signupForm="ngForm" creates a template reference to the form's own tracked state — signupForm.invalid is true whenever any field with a validator (here, required) doesn't currently satisfy it, which is what disables the submit button until the email field has something in it.

Showing a field-specific error

HTML signup.component.html — field feedback
<input name="email" #emailField="ngModel" [(ngModel)]="email" type="email" required>
<p *ngIf="emailField.touched && emailField.invalid">
  Please enter a valid email.
</p>

The same template-reference trick works on an individual input — emailField.touched is true once the user has focused and left the field, which is how you show a validation message only after someone's actually interacted with it, instead of on first render before they've typed anything.

Note: ngModel only works inside Angular's FormsModule — a plain new project's root module needs it imported before [(ngModel)] will do anything, and forgetting that import is one of the most common first-week Angular errors, usually showing up as "Can't bind to 'ngModel' since it isn't a known property."