Introduction to forms in Angular

Handling user input with forms is the cornerstone of many common applications. Applications use forms to enable users log in, to update a profile, to enter sensitive information, and to perform many other data-entry tasks.
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
Reactive and template-driven forms differ, however, in how they do the work of processing and managing forms and form data. Each offers different advantages.
In general:

  • Reactive forms are more robust: they are more scalable, reusable, and testable. If forms are a key part of your application, or you're already using reactive patterns for building your application, use reactive forms.
  • Template-driven forms are useful for adding a simple form to an app, such as an email list signup form. They are easy to add to an app, but they do not scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, use template-driven forms.

Setup in reactive forms

Here is a component with an input field for a single control implemented using reactive forms.





















The form model in the above example is the FormControl instance.





















Setup in template-driven forms

Here is the same component with an input field for a single control implemented using template-driven forms.


















In template-driven forms, the source of truth is the template.
























Form validation

Validation is an integral part of managing any set of forms. Whether you’re checking for required fields or querying an external API for an existing username, Angular provides a set of built-in validators as well as the ability to create custom validators.
  • Reactive forms define custom validators as functions that receive a control to validate.
  • Template-driven forms are tied to template directives, and must provide custom validator directives that wrap validation functions.

For more on form validation, see the Form Validation guide.

Mutability

How changes are tracked plays a role in the efficiency of your application.
  • Reactive forms keep the data model pure by providing it as an immutable data structure. Each time a change is triggered on the data model, the FormControl instance returns a new data model rather than updating the data model directly. This gives you the ability track unique changes to the data model through the control's observable. This allows change detection to be more efficient because it only needs to update on unique changes. It also follows reactive patterns that integrate with observable operators to transform data.
  • Template-driven forms rely on mutability with two-way data binding to update the data model in the component as changes are made in the template. Because there are no unique changes to track on the data model when using two-way data binding, change detection is less efficient at determining when updates are required.
The difference is demonstrated in the examples above using the favorite color input element.
  • With reactive forms, the FormControl instance always returns a new value when the control's value is updated.
  • With template-driven forms, the favorite color property is always modified to its new value.

Scalability

If forms are a central part of your application, scalability is very important. Being able to reuse form models across components is critical.

  • Reactive forms make creating large scale forms easier by providing access to low-level APIs and synchronous access to the form model.
  • Template-driven forms focus on simple scenarios, are not as reusable, abstract away the low-level APIs and access to the form model is provided asynchronously. The abstraction with template-driven forms surfaces in testing also, where testing reactive forms requires less setup and no dependence on the change detection cycle when updating and validating the form and data models during testing.

Reactive Forms

Reactive forms provide a model-driven approach to handling form inputs whose values change over time. This guide shows you how to create and update a simple form control, progress to using multiple controls in a group, validate form values, and implement more advanced forms.
Try the Reactive Forms live-example / download example.

Introduction to reactive forms

Reactive forms use an explicit and immutable approach to managing the state of a form at a given point in time. Each change to the form state returns a new state, which maintains the integrity of the model between changes. Reactive forms are built around observable streams, where form inputs and values are provided as streams of input values, which can be accessed synchronously.
Reactive forms also provide a straightforward path to testing because you are assured that your data is consistent and predictable when requested. Any consumers of the streams have access to manipulate that data safely.

Reactive forms differ from template-driven forms in distinct ways. Reactive forms provide more predictability with synchronous access to the data model, immutability with observable operators, and change tracking through observable streams. If you prefer direct access to modify data in your template, template-driven forms are less explicit because they rely on directives embedded in the template, along with mutable data to track changes asynchronously. See the Forms Overview for detailed comparisons between the two paradigms.

Getting started

This section describes how to add a single form control. In the example, the user enters their name into an input field, captures that input value, and displays the current value of the form control element.

Step 1: Registering the reactive forms module

To use reactive forms, import ReactiveFormsModule from the @angular/forms package and add it to your NgModule's imports array.

Step 2: Generating and importing a new form control

Generate a component for the control.
      
ng generate component NameEditor

Step 3: Registering the control in the template

After you create the control in the component class, you must associate it with a form control element in the template. Update the template with the form control using the formControl binding provided by FormControlDirective included in ReactiveFormsModule.
src/app/name-editor/name-editor.component.html
      
<label>
  Name:
  <input type="text" [formControl]="name">
</label>
Using the template binding syntax, the form control is now registered to the name input element in the template. The form control and DOM element communicate with each other: the view reflects changes in the model, and the model reflects changes in the view.

Displaying the component

The form control assigned to name is displayed when the component is added to a template.
src/app/app.component.html (name editor)
      
<app-name-editor></app-name-editor>

Managing control values

Reactive forms give you access to the form control state and value at a point in time. You can manipulate the current state and value through the component class or the component template. The following examples display the value of the form control instance and change it.

Displaying a form control value

You can display the value in these ways:
  • Through the valueChanges observable where you can listen for changes in the form's value in the template using AsyncPipe or in the component class using the subscribe() method.
  • With the value property. which gives you a snapshot of the current value.
The following example shows you how to display the current value using interpolation in the template.
src/app/name-editor/name-editor.component.html (control value)
      
<p>
  Value: {{ name.value }}
</p>
    

The displayed value changes as you update the form control element.

Replacing a form control value

Reactive forms have methods to change a control's value programmatically, which gives you the flexibility to update the value without user interaction. A form control instance provides a setValue() method that updates the value of the form control and validates the structure of the value provided against the control's structure. For example, when retrieving form data from a backend API or service, use the setValue() method to update the control to its new value, replacing the old value entirely.
The following example adds a method to the component class to update the value of the control to Nancy using the setValue() method.
src/app/name-editor/name-editor.component.ts (update value)
      

updateName() {
  this.name.setValue('Nancy');
}

Grouping form controls

Just as a form control instance gives you control over a single input field, a form group instance tracks the form state of a group of form control instances (for example, a form). Each control in a form group instance is tracked by name when creating the form group. The following example shows how to manage multiple form control instances in a single group.

Displaying the component

To display the ProfileEditor component that contains the form, add it to a component template.
src/app/app.component.html (profile editor)
      
<app-profile-editor></app-profile-editor>
    

ProfileEditor allows you to manage the form control instances for the firstName and lastName controls within the form group instance.

Creating nested form groups

When building complex forms, managing the different areas of information is easier in smaller sections, and some groups of information naturally fall into the same group. Using a nested form group instance allows you to break large forms groups into smaller, more manageable ones.

Step 1: Creating a nested group

Step 2: Grouping the nested form in the template


Partial model updates

When updating the value for a form group instance that contains multiple controls, you may only want to update parts of the model. This section covers how to update specific parts of a form control data model.

Patching the model value

There are two ways to update the model value:

  • Use the setValue() method to set a new value for an individual control. The setValue() method strictly adheres to the structure of the form group and replaces the entire value for the control.
  • Use the patchValue() method to replace any properties defined in the object that have changed in the form model.

Generating form controls with FormBuilder

Creating form control instances manually can become repetitive when dealing with multiple forms. The FormBuilder service provides convenient methods for generating controls.
The following section refactors the ProfileEditor component to use the form builder service to create form control and form group instances.

Simple form validation

Form validation is used to validate user input to ensure it's complete and correct. This section covers adding a single validator to a form control and displaying the overall form status. Form validation is covered more extensively in the Form Validation guide.

Step 1: Importing a validator function

Step 2: Making a field required

The most common validation is making a field required. This section describes how to add a required validation to the firstName control.
In the ProfileEditor component, add the Validators.required static method as the second item in the array for the firstName control.
src/app/profile-editor/profile-editor.component.ts (required validator)
      

profileForm = this.fb.group({
  firstName: ['', Validators.required],
..

Displaying form status

Dynamic controls using form arrays

FormArray is an alternative to FormGroup for managing any number of unnamed controls. As with form group instances, you can dynamically insert and remove controls from form array instances, and the form array instance value and validation status is calculated from its child controls. However, you don't need to define a key for each control by name, so this is a great option if you don't know the number of child values in advance.

Appendix

Reactive forms API

Listed below are the base classes and services used to create and manage form controls.

Template-driven forms

You can build forms by writing templates in the Angular template syntax with the form-specific directives and techniques described in this page.
You can also use a reactive (or model-driven) approach to build forms. However, this page focuses on template-driven forms.
You can build almost any form with an Angular template—login forms, contact forms, and pretty much any business form. You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors, conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.

Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd otherwise wrestle with yourself.


You'll build this form in small steps:
  1. Create the Hero model class.
  2. Create the component that controls the form.
  3. Create a template with the initial form layout.
  4. Bind data properties to each form control using the ngModel two-way data-binding syntax.
  5. Add a name attribute to each form-input control.
  6. Add custom CSS to provide visual feedback.
  7. Show and hide validation-error messages.
  8. Handle form submission with ngSubmit.
  9. Disable the form’s Submit button until the form is valid.

Setup

Create a new project named angular-forms:
      
ng new angular-forms
    

Create the Hero model class

As users enter form data, you'll capture their changes and update an instance of a model. You can't lay out the form until you know what the model looks like.
A model can be as simple as a "property bag" that holds facts about a thing of application importance. That describes well the Hero class with its three required fields (id, name, power) and one optional field (alterEgo).
Using the Angular CLI, generate a new class named Hero:
      
ng generate class Hero
    
With this content:
src/app/hero.ts
      
export class Hero {

  constructor(
    public id: number,
    public name: string,
    public power: string,
    public alterEgo?: string
  ) {  }

}
    
It's an anemic model with few requirements and no behavior. Perfect for the demo.
The TypeScript compiler generates a public field for each public constructor parameter and automatically assigns the parameter’s value to that field when you create heroes.

The alterEgo is optional, so the constructor lets you omit it; note the question mark (?) in alterEgo?.

Create a form component

An Angular form has two parts: an HTML-based template and a component class to handle data and user interactions programmatically. Begin with the class because it states, in brief, what the hero editor can do.
Using the Angular CLI, generate a new component named HeroForm:
      

ng generate component HeroForm

Revise app.module.ts

app.module.ts defines the application's root module. In it you identify the external modules you'll use in the application and declare the components that belong to this module, such as the HeroFormComponent.
Because template-driven forms are in their own module, you need to add the FormsModule to the array of imports for the application module before you can use forms.

Revise app.component.html

AppComponent is the application's root component. It will host the new HeroFormComponent.
Replace the contents of its template with the following:
src/app/app.component.html
      

<app-hero-form></app-hero-form>

Add powers with *ngFor

The hero must choose one superpower from a fixed list of agency-approved powers. You maintain that list internally (in HeroFormComponent).
You'll add a select to the form and bind the options to the powers list using ngFor, a technique seen previously in the Displaying Data page.
Add the following HTML immediately below the Alter Ego group:
src/app/hero-form/hero-form.component.html (powers)
      

<div class="form-group">
  <label for="power">Hero Power</label>
  <select class="form-control" id="power" required>
    <option *ngFor="let pow of powers" [value]="pow">{{pow}}</option>
  </select>
</div>

Add custom CSS for visual feedback

You can mark required fields and invalid data at the same time with a colored bar on the left of the input box:

Submit the form with ngSubmit

The user should be able to submit this form after filling it in. The Submit button at the bottom of the form does nothing on its own, but it will trigger a form submit because of its type (type="submit").
A "form submit" is useless at the moment. To make it useful, bind the form's ngSubmit event property to the hero form component's onSubmit() method:
src/app/hero-form/hero-form.component.html (ngSubmit)
      

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

Summary

The Angular form discussed in this page takes advantage of the following framework features to provide support for data modification, validation, and more:
  • An Angular HTML form template.
  • A form component class with a @Component decorator.
  • Handling form submission by binding to the NgForm.ngSubmit event property.
  • Template-reference variables such as #heroForm and #name.
  • [(ngModel)] syntax for two-way data binding.
  • The use of name attributes for validation and form-element change tracking.
  • The reference variable’s valid property on input controls to check if a control is valid and show/hide error messages.
  • Controlling the Submit button's enabled state by binding to NgForm validity.
  • Custom CSS classes that provide visual feedback to users about invalid controls.

Form Validation

Improve overall data quality by validating user input for accuracy and completeness.
This page shows how to validate user input in the UI and display useful validation messages using both reactive and template-driven forms. It assumes some basic knowledge of the two forms modules.
If you're new to forms, start by reviewing the Forms and Reactive Forms guides.

Template-driven validation

To add validation to a template-driven form, you add the same validation attributes as you would with native HTML form validation. Angular uses directives to match these attributes with validator functions in the framework.

Every time the value of a form control changes, Angular runs validation and generates either a list of validation errors, which results in an INVALID status, or null, which results in a VALID status.

Note the following:
  • The <input> element carries the HTML validation attributes: required and minlength. It also carries a custom validator directive, forbiddenName. For more information, see Custom validators section.
  • #name="ngModel" exports NgModel into a local variable called name. NgModel mirrors many of the properties of its underlying FormControl instance, so you can use this in the template to check for control states such as valid and dirty. For a full list of control properties, see the AbstractControl API reference.
  • The *ngIf on the <div> element reveals a set of nested message divs but only if the name is invalid and the control is either dirty or touched.
  • Each nested <div> can present a custom message for one of the possible validation errors. There are messages for required, minlength, and forbiddenName.

Why check dirty and touched?

You may not want your application to display errors before the user has a chance to edit the form. The checks for dirty and touched prevent errors from showing until the user does one of two things: changes the value, turning the control dirty; or blurs the form control element, setting the control to touched.

Reactive form validation

In a reactive form, the source of truth is the component class. Instead of adding validators through attributes in the template, you add validator functions directly to the form control model in the component class. Angular then calls these functions whenever the value of the control changes.

Validator functions

There are two types of validator functions: sync validators and async validators.
  • Sync validators: functions that take a control instance and immediately return either a set of validation errors or null. You can pass these in as the second argument when you instantiate a FormControl.
  • Async validators: functions that take a control instance and return a Promise or Observable that later emits a set of validation errors or null. You can pass these in as the third argument when you instantiate a FormControl.
Note: for performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.

Built-in validators

You can choose to write your own validator functions, or you can use some of Angular's built-in validators.

The same built-in validators that are available as attributes in template-driven forms, such as required and minlength, are all available to use as functions from the Validators class. For a full list of built-in validators, see the Validators API reference.

Custom validators

Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
Consider the forbiddenNameValidator function from previous examples in this guide

Control status CSS classes

Like in AngularJS, Angular automatically mirrors many control properties onto the form control element as CSS classes. You can use these classes to style form control elements according to the state of the form. The following classes are currently supported:
  • .ng-valid
  • .ng-invalid
  • .ng-pending
  • .ng-pristine
  • .ng-dirty
  • .ng-untouched
  • .ng-touched
The hero form uses the .ng-valid and .ng-invalid classes to set the color of each form control's border.
forms.css (status classes)
      

.ng-valid[required], .ng-valid.required  {
  border-left: 5px solid #42A948; /* green */
}

.ng-invalid:not(form)  {
  border-left: 5px solid #a94442; /* red */
}

Dynamic Forms

Building handcrafted forms can be costly and time-consuming, especially if you need a great number of them, they're similar to each other, and they change frequently to meet rapidly changing business and regulatory requirements.
It may be more economical to create the forms dynamically, based on metadata that describes the business object model.
This cookbook shows you how to use formGroup to dynamically render a simple form with different control types and validation. It's a primitive start. It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience. All such greatness has humble beginnings.
The example in this cookbook is a dynamic form to build an online application experience for heroes seeking employment. The agency is constantly tinkering with the application process. You can create the forms on the fly without changing the application code.
See the live example / download example.

Bootstrap

Start by creating an NgModule called AppModule.

This cookbook uses reactive forms.