Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
Angular is a popular front-end framework made by Google. Like other popular front-end frameworks, it uses a component-based architecture to structure apps.
In this article, we’ll look at the basics of creating forms with Angular.
Different Kinds of Forms
Angular provides 2 different approaches to handling user input through forms.
They are either reactive or template-driven. They both capture events from the view, validate their user input, and create a form model and data model to update, and provide a way to track changes.
Reactive forms are more robust. They’re more scalable, reusable and testable.
Template-driven forms are useful for adding a simple form to an app. They’re easy to add, but they don’t scale as well as reactive forms.
Common Foundation
Both kinds of forms share the following building blocks:
-
FormControl
— tracks the value and validation status of individual form controls -
FormGroup
— tracks the same value and status for a collection of form controls -
FormArray
— tracks the same value and status for an array of form control.s. -
ControlValueAccessor
— creates a bridge between AngularFormControl
instances and native DOM elements.
Form Model Setup
We can set up the form model of a Reactive form by setting the formControl
property of an input element.
For example, we can write the following code:
app.module.ts
:
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { ReactiveFormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, ReactiveFormsModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.ts
:
import { Component } from "@angular/core";
import { FormControl } from "@angular/forms";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
nameControl = new FormControl("");
}
app.component.html
:
<input type="text" [formControl]="nameControl" />
In the code above, we added the ReactiveFormsModule
to our AppModule
.
Then we created nameControl
in AppComponent
.
Finally, we set the formControl
property to nameControl
to connect the input to the FomControl
.
Setup in Template-Driven Forms
We can use the ngModel
directive to bind our input value to the a field in our component.
For example, we can write the following:
app.module.ts
:
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { AppComponent } from "./app.component";
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, FormsModule],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.ts
:
import { Component } from "@angular/core";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
name: string;
}
app.component.html
:
<input type="text" [(ngModel)]="name" />
In the code above, we imported the FormsModule
in app.module.ts
.
Then we created the name
field in AppComponent
to bind the data to the field.
Then we bind our input’s value to name
via ngModel
.
Both Reactive and Template-Driven forms provide 2 way binding between the model and the input.
Data Flow in Reactive Forms
From view to model, the data flow follows the following steps:
- User types something into the input
- Form input emits an input event
- Control value accessor listens for events on the form input and sends the new value to the
FormControl
instance. - The
FormControl
instance emits the new value through thevalueChanges
observable. - Subscribes to
valueChanges
receive the new value
From model to view, the flow is as follows:
- The user calls the
nameControl.setValue
method to update theFormControl
value - The
FormControl
instance emits the new value through thevalueChanges
observable. - Any subscribers to the
valueChanges
observable receive the new value - The control value accessor on the form input element updates the element with the new value
Data Flow in Template-Driven Forms
The data from the view to the model in template-driven forms are as follows:
- User types something into the input element.
- The input element emits an input event with the value typed in
- The control value accessor attached to the input triggers the
setValue
method on theFormControl
instance - The
FormControl
instance emits the new value throughvalueChanges
observable - Any subscribers to the
valueChanges
observable receives the new value - The control value accessor also calls
NgModel.viewToModelUpdate
method which emits anngModelChange
event -
ngModelChanges
triggers model value update
From model to view, the flow is as follows:
- The value is updated in the component
- Change detection begins
-
ngOnChanges
is called on theNgModel
directive instance because the input value has changed -
ngOnChanges
queues an async task to set the value of theFormControl
instance - Change detection completes
- The task to set
FormControl
instance value is run on next tick -
FormControl
instance emits the latest change through thevalueChanges
observable - Any subscribers to the
valueChanges
observable receives the new value - Control value accessor updates the form input element in the view
Conclusion
We can create Angular forms as Reactive or Template-Driven forms.
Reactive forms are more robust and Template-Driven forms are suitable for simple forms.
They both have the same parts. The control value accessor takes the input value and sets it to the model.