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
export class SignupComponent {
email = '';
onSubmit() {
console.log('Submitted email:', this.email);
}
}
<form (ngSubmit)="onSubmit()"> <input name="email" [(ngModel)]="email" type="email"> <button type="submit">Sign up</button> </form>
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
<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
<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.
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."