2.0 Differences Between Template-Driven and Reactive Forms
-- 2.1 Common foundation of Reactive Form and Template-Driven Form
-- 2.2 Advantages and Disadvantages of Reactive Forms
3.0 Application process
-- 3.1 Registering the ReactiveFormsModule in the module.ts
-- 3.2 A new FormControl in the component.ts
-- 3.3 Registering the control in the template .html
-- 3.3 Displaying a form control value
-- 3.4 Managing control value
-- -- 3.4.1 Displaying a form control value
-- -- 3.4.2 Replacing a form control value component.ts
-- 3.5 Grouping form controls
-- -- 3.5.1 Creating a FormGroup instance in component.ts
-- -- 3.5.2 Associating the FormGroup model and view in .html
-- 3.6 Creating nested form groups
-- -- 3.6.1 Creating a nested group
-- -- 3.6.2 Grouping the nested form in the template
-- 3.7 Partial model updates(Patching the model value)
-- 3.8 Generating form controls with FormBuilder
-- 3.9 Simple form validation
-- 3.9 Complete Form Validation
-- -- 3.9.1 Validator functions
-- -- 3.9.2 Built-in validators
-- -- 3.9.3 Custom validators
-- -- -- 3.9.3.1 Adding to reactive forms
-- -- -- 3.9.3.2 Adding to template-driven forms (not complete, not related right now)
-- -- 3.9.4 Control status CSS classes
-- -- 3.9.5 Async Validation (not complete)
-- 3.10 Displaying form status
-- 3.11 Dynamic controls using FormArray
Angular forms work in two different ways, either as Template Driven Forms or as Reactive Forms - also sometimes called Model-Driven Forms
1.0 What Are Angular Reactive Forms?
Reactive forms are also known as model-driven forms. This means that the HTML content changes depending on the code in the component. [2]
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.[1]
2.0 Differences Between Template-Driven and Reactive Forms [2][5]
Template-driven forms use the FormsModule, while reactive forms use the ReactiveFormsModule.
Template-driven forms are asynchronous, while reactive forms are synchronous.
In template-driven forms, most of the interaction occurs in the template, while in reactive-driven forms, most of the interaction occurs in the component.
Template-driven forms
Reactive forms
Module
FormsModule
ReactiveFormsModule
Sync/Predictability
Asynchronous
Synchronous
Interactions/structures
in template .html
in component .ts
Form validation
Directives
Functions
Mutability
immutability with observable operators
Mutable
2.1 Common foundation of Reactive Form and Template-Driven Form[5]
Building Blocks
Contents
FormControl
tracks the value and validation status of an individual form control
FormGroup
tracks the same values and status for a collection of form controls.
FormArray
tracks the same values and status for an array of FormControl, FormGroup or FormArray instances.
ControlValueAccessor
creates a bridge between Angular FormControl instances and native DOM elements.
2.2 Advantages and Disadvantages of Reactive Forms [2]
Features
Ad\Dis
Reasons
Advantages
Easier to write Unit Tests
all the form code and functionality is contained in the component
Disadvantages
3.0 Application process (Taken from Demo 1)
3.1 Registering the ReactiveFormsModule in the module.ts
import { ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
// other imports ...
ReactiveFormsModule
],
})
export class AppModule { }
3.2 A new FormControl in the component.ts
export class NameEditorComponent {
name = new FormControl('');
}
The FormControl class is the basic Building Block when using reactive forms
To register a single form control, import the FormControl class into your component and create a new instance of the form control to save as a class property.
Use the constructor of FormControl to set its initial value, which in this case is an empty string. - By creating these controls in your component class, you get immediate access to listen for, update, and validate the state of the form input.
A form control instance gives you control over a single input field
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
3.4 Managing Control value
3.4.1 Displaying a form control value
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.
<p>
Value: {{ name.value }}
</p>
3.4.2 Replacing a form control in value component.ts
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.
When using the setValue() method with a form group or form array instance, the value needs to match the structure of the group or array.
updateName() {
this.name.setValue('Nancy');
}
3.5 Grouping form controls
A form group instance tracks the form state of a group of form control instances
Each control in a form group instance is tracked by name when creating the form group
3.5.1 Creating a FormGroup instance in component.ts
import { FormGroup, FormControl } from '@angular/forms';
export class ProfileEditorComponent {
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
});
}
3.5.2 Associating the FormGroup model and view in .html
<form [formGroup]="profileForm">
<label>
First Name:
<input type="text" formControlName="firstName">
</label>
<label>
Last Name:
<input type="text" formControlName="lastName">
</label>
</form>
A form group tracks the status and changes for each of its controls
If one of the controls changes, the parent control also emits a new status or value change.
A form group contains a group of controls
The profileformFormGroup is bound to the form element with the FormGroupdirective
The formControlName input provided by the FormControlName directive binds each individual input to the form control defined in FormGroup
3.6 Creating nested form groups
Using a nested form group instance allows you to break large forms groups into smaller, more manageable ones
3.6.1 Creating a nested group
export class ProfileEditorComponent {
profileForm = new FormGroup({ <==== 1st layer of form group
firstName: new FormControl(''),
lastName: new FormControl(''),
address: new FormGroup({ <==== nested form group
street: new FormControl(''),
city: new FormControl(''),
state: new FormControl(''),
zip: new FormControl('')
})
});
}
The address element in the form group is a child of the overall profileForm element in the form group, the same rules apply with value and status changes
Changes in status and value from the nested form grouppropagate to the parent form group, maintaining consistency with the overall model.
3.7 Partial model updates (Patching the model value)
Only updating parts of the model values of the whole FormGroup
Use the setValue() method to set a new value for an individual control. It 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.
compare with the old settings, there is no need to write FormContrl for each element
profileForm = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl(''),
address: new FormGroup({
street: new FormControl(''),
city: new FormControl(''),
state: new FormControl(''),
zip: new FormControl('')
})
});
3.9 Simple form validation
In order to validate user input to ensure it's complete and correct
Further details could be found in Form Validation
HTML5 has a set of built-in attributes that you can use for native validation,
including
required,
minlength,
maxlength.
Use these HTML5 validation attributes in combination with the built-in validators provided by Angular's reactive forms, notice the required in the following code
Sync validators:
-- takes a control instance
-- immediately return either a set of validation errors or null
-- passed as 2nd argument when instantiate a FormControl
Async validators:
-- takes a control instance
-- return a Promise or Observable that later emits a set of validation errors or null
-- passed as 3rd argument when instantiate a FormControl
Note: for performance reasons, Angular only runs async validators if all sync validators pass.
ngOnInit(): void {
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [ <==== passed as the 2nd arguments
Validators.required, <==== passed as the 2nd arguments
Validators.minLength(4), <==== passed as the 2nd arguments
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator. <==== passed as the 2nd arguments
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'power': new FormControl(this.hero.power, Validators.required)
});
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
The name control sets up two built-in validators—Validators.required and Validators.minLength(4)
and one custom validator, forbiddenNameValidator.
multiple validators by passing the functions in as an array.
In a reactive form, you can always access any form control through the get method on its parent group
<input id="name" class="form-control"
formControlName="name" required >
<div *ngIf="name.invalid && (name.dirty || name.touched)"
class="alert alert-danger">
<div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 4 characters long.
</div>
<div *ngIf="name.errors.forbiddenName">
Name cannot be Bob.
</div>
</div>
3.9.3 Custom validators
Here is the Custom Validator for the above forbiddenNameValidator
The function is actually a factory that takes a regular expression to detect a specific forbidden name and returns a validator function. It returns either null if the control value is valid or a validation error object
Combing with the above case, the forbidden name is "bob", so the validator will reject any hero name containing "bob".
3.9.3.1 Adding to reactive forms
Pass the function directly to the FormControl.
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'power': new FormControl(this.hero.power, Validators.required)
});
3.9.3.2 Adding to template-driven forms (not complete, not related right now)
can't pass the validator in like you can for reactive forms
need to add a directive to the template.
The corresponding ForbiddenValidatorDirective serves as a wrapper around the forbiddenNameValidator.
The directive registers itself with the NG_VALIDATORSprovider, a provider with an extensible collection of validators.
3.9.4 Control status CSS classes
The following classes are currently supported:
.ng-valid
.ng-invalid
.ng-pending
.ng-pristine
.ng-dirty
.ng-untouched
.ng-touched
3.9.5 Async Validation (not complete)
synchronous validators: ValidatorFn and Validator
asynchronous validators: AsyncValidatorFn and AsyncValidator.
asynchronous validators must return a Promise or an Observable,
The observable returned must be finite, meaning it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as first, last, take, or takeUntil.
asynchronous validation happens after the synchronous validation; is performed only if the synchronous validation is successful
3.10 Displaying form status
Simply using interpolation.
<p>
Form Status: {{ profileForm.status }}
</p>
3.11 Dynamic controls using FormArray
FormArray is an alternative to FormGroup
For FormArray 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.
Use the FormBuilder.array() method to define the array,
Use the FormBuilder.control() method to populate the array with an initial control.
import { FormArray } from '@angular/forms';
profileForm = this.fb.group({ <====
firstName: ['', Validators.required],
lastName: [''],
address: this.fb.group({
street: [''],
city: [''],
state: [''],
zip: ['']
}),
aliases: this.fb.array([ <==== see the differences
this.fb.control('') <==== only one
])
});
get aliases() {
return this.profileForm.get('aliases') as FormArray;
}
// Because the returned control is of the type AbstractControl, you need to provide an explicit type to access the method syntax for the form array instance.
addAlias() {
this.aliases.push(this.fb.control(''));
}
<div formArrayName="aliases">
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
<div *ngFor="let address of aliases.controls; let i=index">
<!-- The repeated alias template -->
<label>
Alias:
<input type="text" [formControlName]="i">
</label>
</div>
</div>
Related articles
Angular Form - Template Driven Form Form Validation Comparisons between Reactive Forms and Template Driven Forms
Guidelines
Angular forms work in two different ways, either as
Template Driven Forms
or asReactive Forms
- also sometimes calledModel-Driven Forms
1.0 What Are Angular Reactive Forms?
Reactive forms are also known as
model-driven
forms. This means that the HTML content changes depending on the code in thecomponent
. [2]Demo 1 [1] Demo 2 for comparisons
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.[1]
2.0 Differences Between Template-Driven and Reactive Forms [2][5]
FormsModule
, while reactive forms use theReactiveFormsModule
.asynchronous
, while reactive forms aresynchronous
.template
, while in reactive-driven forms, most of the interaction occurs in thecomponent
.2.1 Common foundation of Reactive Form and Template-Driven Form[5]
2.2 Advantages and Disadvantages of Reactive Forms [2]
Advantages
Unit Tests
component
3.0 Application process (
Taken from Demo 1
)3.1 Registering the ReactiveFormsModule in the module.ts
3.2 A new FormControl in the component.ts
basic Building Block
when usingreactive forms
constructor
of FormControl to set its initial value, which in this case is an empty string. - By creating these controls in your component class, you get immediate access tolisten for
,update
, andvalidate
the state of the form input.form control instance
gives you control overa single input field
3.3 Registering the control in the template .html
3.4 Managing Control value
3.4.1 Displaying a form control value
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.value
property. which gives you a snapshot of the current value.3.4.2 Replacing a form control in value component.ts
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.When using the setValue() method with a form group or form array instance, the value needs to match the structure of the group or array.
3.5 Grouping form controls
form group instance
tracks the form state ofa group of form control instances
tracked
byname
when creating the form group3.5.1 Creating a FormGroup instance in component.ts
3.5.2 Associating the FormGroup model and view in .html
each of its controls
FormGroup
directive3.6 Creating nested form groups
Using a nested form group instance allows you to break large forms groups into smaller, more manageable ones
3.6.1 Creating a nested group
from
thenested form group
propagate
to theparent
form group, maintaining consistency with the overall model.3.6.2 Grouping the nested form in the template
3.7 Partial model updates (Patching the model value)
Only updating parts of the model values of the whole FormGroup
setValue()
method to set a new value for an individual control. It strictly adheres to the structure of the form group and replaces the entire value for the control.patchValue()
method to replace any properties defined in the object that have changed in the form model.3.8 Generating form controls with FormBuilder
The FormBuilder service provides methods for generating controls
compare with the old settings, there is no need to write FormContrl for each element
3.9 Simple form validation
In order to validate user input to ensure it's complete and correct Further details could be found in Form Validation
Here is only a single validator
HTML5 has a set of built-in attributes that you can use for native validation, including
required
,minlength
,maxlength
.Use these HTML5 validation attributes in combination with the built-in validators provided by Angular's reactive forms, notice the
required
in the following code3.9 Complete Form Validation
3.9.1 Validator functions
Sync validators: -- takes a control instance -- immediately return either a set of validation errors or null -- passed as
2nd
argument when instantiate aFormControl
Async validators: -- takes a control instance -- return a Promise or Observable that later emits a set of validation errors or null -- passed as
3rd
argument when instantiate aFormControl
Note
: for performance reasons, Angular only runs async validators if all sync validators pass.3.9.2 Built-in validators all built-in validators of Angular
e.g.
required
minlength
all built-in validators of Angular[6]two built-in validators
—Validators.required
andValidators.minLength(4)
custom validator
,forbiddenNameValidator
.multiple validators
by passing the functions inas an array
.3.9.3 Custom validators
Here is the Custom Validator for the above forbiddenNameValidator
forbidden-name.directive.ts (forbiddenNameValidator)
3.9.3.1 Adding to reactive forms
3.9.3.2 Adding to template-driven forms (not complete, not related right now)
wrapper
around the forbiddenNameValidator.NG_VALIDATORS
provider
, a provider with an extensible collection of validators.3.9.4 Control status CSS classes
The following classes are currently supported:
3.9.5 Async Validation (not complete)
finite
, meaning it must complete at some point. To convert an infinite observable into a finite one,pipe
the observable through afiltering operator
such as first, last, take, or takeUntil.after
the synchronous validation; is performedonly if
the synchronous validation is successful3.10 Displaying form status
Simply using interpolation.
3.11 Dynamic controls using FormArray
FormArray
is an alternative toFormGroup
For FormArray 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.
FormBuilder.array()
method to define the array,FormBuilder.control()
method to populate the array with an initial control.Reference
[1] https://angular.io/guide/reactive-forms [2] https://code.tutsplus.com/tutorials/angular-form-validation-with-reactive-and-template-driven-forms--cms-32131 [3] https://malcoded.com/posts/angular-fundamentals-reactive-forms/ [4] https://alligator.io/angular/reactive-forms-introduction/ [5] https://angular.io/guide/forms-overview [6] https://angular.io/api/forms/Validators