Before we start building an Angular form, it's worth noting that Angular offers two distinct ways to work with forms.

  • Template-driven approach. The form structure lives directly in the HTML template, and Angular derives the form model from the markup. This is the simplest route for getting up and running with forms.
  • Reactive approach. The form is constructed programmatically in the component class using TypeScript. This method is more explicit and gives you finer control over customization. You might want to review What is RxJS first.

For this guide, we'll go with the template-driven method.
If event binding is unfamiliar territory, check out this simple app that covers the basics.

Building a Template-driven Angular Form

Setting up the form

Let's begin with the familiar <form> HTML element.

// app.component.html

<form>
  <label for="userInput">Add Task</label>
  <input
    placeholder="Write a task"
    name="userInput"
    required
  />
  <label for="date">By when</label>
  <input type="date" name="date" />
  <button type="submit">Add</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Notice there's no action or method attribute. In a conventional HTML form, those would send data to a server — but here, Angular takes over that responsibility.

Here's what the app currently looks like:

To Do App using Angular Forms - part 1 — figure 1

I'll skip the CSS details in this post, but the complete source will be linked at the end.

A crucial step: confirm FormsModule is imported in app.module.ts. Without it, Angular won't process forms. Your module file should resemble this:

//  app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  imports:      [ BrowserModule, FormsModule ],
  declarations: [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

With FormsModule in place, Angular automatically builds a JavaScript model of the form the moment it finds a form tag in your template.

Think of that JavaScript model as a plain object — a collection of key-value pairs that mirror the inputs inside the form.

Registering form controls

Next, we have to tell Angular which elements are actual form controls. Angular won't assume every element is a control, since not all of them need to be.

Two pieces of information are required to mark an element as a control:

  1. ngModel. Adding ngModel to an element designates it as a control. This directive is also frequently used for two-way data binding.
  2. Control name. The standard HTML name attribute is used to give the control a unique identifier.
// app.component.html

<form>
  <label for="userInput">Add Task</label>
  <input 
    placeholder="Write a task" 
    ngModel 
    name="userInput" 
    required 
  />
  <label for="date">By when</label>
  <input  
    type="date"  
    name="date" 
    ngModel 
  />
  <button type="submit">Add</button>
</form>
Enter fullscreen mode Exit fullscreen mode

After these additions, the JavaScript model now has two registered controls.

The name HTML attribute was used to assign each control its identifier, so that requirement is sorted out.

Handling form submission

Angular leverages the native submission behavior of HTML forms. A click on a submit button triggers a submit event, which would normally send a request — but Angular intercepts this flow.

To listen for submission, Angular provides the ngSubmit directive on the form tag. This is an event listener that fires each time the form is submitted.
To confirm it's working, let's attach the event listener to the form element.

// app.component.html

<form (ngSubmit)="onSubmit()">
  ...
</form>
Enter fullscreen mode Exit fullscreen mode

The ngSubmit listener invokes the onSubmit() method, which we need to define in the component. For now, that method simply writes a string to the browser console.

app.component.ts

export class AppComponent {
  ...

  onSubmit() {
    console.log('Submitted');
  }
}

Enter fullscreen mode Exit fullscreen mode

When the add button is clicked, the message "Submitted" appears in the console. That's progress — Angular is handling the submission, or at minimum, ngSubmit is triggering the event correctly.

Getting data from Angular Forms

We now need a way to access the data contained in the Angular form model. Essentially, we want to retrieve the automatically created JavaScript object.

To get a handle on that object, we set a local template reference to ngForm on the form tag: #myForm="ngForm". This instructs Angular to expose the auto-generated form model.

// app.component.html

<form (ngSubmit)="onSubmit(myForm)" #myForm="ngForm">
  ...
</form>
Enter fullscreen mode Exit fullscreen mode

Note that the local reference myForm is passed into the onSubmit method.

The onSubmit method in app.component.ts must be updated to accept a parameter of type NgForm.

app.component.ts

import { NgForm } from '@angular/forms';
...

export class AppComponent {
  ...

  onSubmit(form: NgForm) {
    console.log('Submitted', form);
  }
}

Enter fullscreen mode Exit fullscreen mode

Once the form is submitted, you'll see the NgForm object logged to the console. Take some time to poke around and inspect its structure — exploring is one of the best ways to learn.

The screenshot below shows a snippet of that form object.
You'll notice the controls and value keys right away.
To Do App using Angular Forms - part 1 — figure 2

The value property holds the current values for the controls we registered earlier: userInput and date.

If you log form.form.value to the console, you'll get an object like this:

{
    "userInput": "some user input abc",
    "date": "2022-02-09"
}
Enter fullscreen mode Exit fullscreen mode

One last note: if you want to use HTML5 validation, Angular keeps it switched off by default. You'll need to add ngNativeValidate to the form tag in the template to enable it.