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.
- 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
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, importReactiveFormsModule
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 theformControl
binding provided by FormControlDirective
included in ReactiveFormsModule
.<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 toname
is displayed when the component is added to a template. <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 usingAsyncPipe
or in the component class using thesubscribe()
method. - With the
value
property. which gives you a snapshot of the current 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 asetValue()
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.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 theProfileEditor
component that contains the form, add it to a component template.<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. ThesetValue()
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. TheFormBuilder
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 thefirstName
control.In the
ProfileEditor
component, add the Validators.required
static method as the second item in the array for the firstName
control.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:
- Create the
Hero
model class. - Create the component that controls the form.
- Create a template with the initial form layout.
- Bind data properties to each form control using the
ngModel
two-way data-binding syntax. - Add a
name
attribute to each form-input control. - Add custom CSS to provide visual feedback.
- Show and hide validation-error messages.
- Handle form submission with ngSubmit.
- Disable the form’s Submit button until the form is valid.
Setup
Create a new project namedangular-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: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:
<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 (inHeroFormComponent
).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:
<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:<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.
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
andminlength
. It also carries a custom validator directive,forbiddenName
. For more information, see Custom validators section.
-
#name="ngModel"
exportsNgModel
into a local variable calledname
.NgModel
mirrors many of the properties of its underlyingFormControl
instance, so you can use this in the template to check for control states such asvalid
anddirty
. For a full list of control properties, see the AbstractControl API reference.
-
The
*ngIf
on the<div>
element reveals a set of nested messagedivs
but only if thename
is invalid and the control is eitherdirty
ortouched
.
-
Each nested
<div>
can present a custom message for one of the possible validation errors. There are messages forrequired
,minlength
, andforbiddenName
.
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 aFormControl
.
-
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 aFormControl
.
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 guideControl 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
.ng-valid
and .ng-invalid
classes to
set the color of each form control's border..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
Bootstrap
Start by creating anNgModule
called AppModule
.This cookbook uses reactive forms.
0 comments:
Post a Comment