The Ultimate Guide to Forms in Angular
Reading Time: 20 minutesForms are probably the most crucial aspect of your web application. While we often get events from clicking on links or moving the mouse, it’s through forms where we get the majority of our rich data input from users.
On the surface, forms seem straightforward: you make an input
tag, the user fills it out, and hits submit. How hard could it be?
It turns out, forms can be very complex. Here’s a few reasons why:
- Form inputs are meant to modify data, both on the page and the server
- Changes often need to be reflected elsewhere on the page
- Users have a lot of leeway in what they enter, so you need to validate values
- The UI needs to clearly state expectations and errors, if any
- Dependent fields can have complex logic
- We want to be able to test our forms, without relying on DOM selectors
Thankfully, Angular has tools to help with all of these things.
FormControl
s encapsulate the inputs in our forms and give us objects to work with themValidator
s give us the ability to validate inputs, any way we’d like- Observers let us watch our form for changes and respond accordingly
In this chapter we’re going to walk through building forms, step by step. We’ll start with some simple forms and build up to more complicated logic.
FormControl
s and FormGroup
s
The two fundamental objects in Angular forms are FormControl
and FormGroup
.
FormControl
A FormControl
represents a single input field - it is the smallest unit of an Angular form.
FormControl
s encapsulate the field’s value, and states such as being valid, dirty (changed), or has errors.
For instance, here’s how we might use a FormControl
in TypeScript:
// create a new FormControl with the value "Nate"
let nameControl = new FormControl("Nate");
let name = nameControl.value; // -> Nate
// now we can query this control for certain values:
nameControl.errors // -> StringMap<string, any> of errors
nameControl.dirty // -> false
nameControl.valid // -> true
// etc.
To build up forms we create FormControl
s (and groups of FormControl
s) and then attach metadata and logic to them.
Like many things in Angular, we have a class (FormControl
, in this case) that we attach to the DOM with an attribute (formControl
, in this case). For instance, we might have the following in our form:
<!-- part of some bigger form -->
<input type="text" [formControl]="name" />
This will create a new FormControl
object within the context of our form
. We’ll talk more about how that works below.
FormGroup
Most forms have more than one field, so we need a way to manage multiple FormControl
s. If we wanted to check the validity of our form, it’s cumbersome to iterate over an array of FormControl
s and check each FormControl
for validity. FormGroup
s solve this issue by providing a wrapper interface around a collection of FormControl
s.
Here’s how you create a FormGroup
:
let personInfo = new FormGroup({
firstName: new FormControl("Nate"),
lastName: new FormControl("Murray"),
zip: new FormControl("90210")
})
FormGroup
and FormControl
have a common ancestor (AbstractControl
). That means we can check the status
or value
of personInfo
just as easily as a single FormControl
:
personInfo.value; // -> {
// firstName: "Nate",
// lastName: "Murray",
// zip: "90210"
// }
// now we can query this control group for certain values, which have sensible
// values depending on the children FormControl's values:
personInfo.errors // -> StringMap<string, any> of errors
personInfo.dirty // -> false
personInfo.valid // -> true
// etc.
Notice that when we tried to get the value
from the FormGroup
we received an object with key-value pairs. This is a really handy way to get the full set of values from our form without having to iterate over each FormControl
individually.
Our First Form
There are lots of moving pieces to create a form, and several important ones we haven’t touched on. Let’s jump in to a full example and I’ll explain each piece as we go along.
Most of the code for each example can be found inline on this post, however,
if you'd like a downloadable version of the full-code examples enter
your email below:
Here’s a screenshot of the very first form we’re going to build:

In our imaginary application we’re creating an e-commerce-type site where we’re listing products for sale. In this app we need to store the product’s SKU, so let’s create a simple form that takes the SKU as the only input field.
Our form is super simple: we have a single input for sku
(with a label) and a submit button.
Let’s turn this form into a Component. If you recall, there are three parts to defining a component:
- Configure the
@Component()
decorator - Create the template
- Implement custom functionality in the component definition class
Let’s take these in turn:
Loading the FormsModule
In order to use the new forms library we need to first make sure we import the forms library in our NgModule
.
There are two ways of using forms in Angular and we’ll talk about them both in this chapter: using FormsModule
or using ReactiveFormsModule
. Since we’ll use both, we’ll import them both into our module. To do this, we do the following in our app.ts
where we bootstrap the app:
// app.module.ts
import {
FormsModule,
ReactiveFormsModule
} from '@angular/forms';
// farther down...
@NgModule({
declarations: [
AppComponent,
DemoFormSkuComponent,
// ... our declarations here
],
imports: [
BrowserModule,
FormsModule, // <-- add this
ReactiveFormsModule // <-- and this
],
bootstrap: [ AppComponent ]
})
class AppModule {}
This ensures that we’re able to use the form directives in our views. At the risk of jumping ahead, the FormsModule
gives us template driven directives such as:
ngModel
andNgForm
Whereas ReactiveFormsModule
gives us reactive driven directives like
formControl
andngFormGroup
… and several more. We haven’t talked about how to use these directives or what they do, but we will shortly. For now, just know that by importing FormsModule
and ReactiveFormsModule
into our NgModule
means we can use any of the directives in that list in our view template or inject any of their respective providers into our components.
Reactive- vs. template-driven Forms
Angular allows you to define forms in two different ways: “reactive” or “template” driven. You can see a comparison of two ways here. Rather than describe how they’re different, we’re going to show you examples of different ways you can build forms - then you can decide which is right for your application.
Simple SKU Form: @Component Decorator
First, let’s start by creating what’s called a “template driven” form. Starting with our component:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-demo-form-sku',
templateUrl: './demo-form-sku.component.html',
Here we define a selector
of app-demo-form-sku
. If you recall, selector
tells Angular what elements this component will bind to. In this case we can use this component by having a app-demo-form-sku
tag like so:
<app-demo-form-sku></app-demo-form-sku>
Simple SKU Form: template
Let’s look at our template:
<div class="ui raised segment">
<h2 class="ui header">Demo Form: Sku</h2>
<form #f="ngForm"
(ngSubmit)="onSubmit(f.value)"
class="ui form">
<div class="field">
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
name="sku" ngModel>
</div>
<button type="submit" class="ui button">Submit</button>
</form>
</div>
form
& NgForm
Now things get interesting: because we imported FormsModule
, that makes NgForm
available to our view. Remember that whenever we make directives available to our view, they will get attached to any element that matches their selector
.
NgForm
does something handy but non-obvious: it includes the form
tag in its selector (instead of requiring you to explicitly add ngForm
as an attribute). What this means is that if you import FormsModule
, NgForm
will get automatically attached to any <form>
tags you have in your view. This is really useful but potentially confusing because it happens behind the scenes.
There are two important pieces of functionality that NgForm
gives us:
- A
FormGroup
namedngForm
- A
(ngSubmit)
output
You can see that we use both of these in the <form>
tag in our view:
<form #f="ngForm"
(ngSubmit)="onSubmit(f.value)"
First we have #f="ngForm"
. The #v="thing"
syntax says that we want to create a local variable for this view.
Here we’re creating an alias to ngForm
, for this view, bound to the variable #f
. Where did ngForm
come from in the first place? It came from the NgForm
directive.
And what type of object is ngForm
? It is a FormGroup
. That means we can use f
as a FormGroup
in our view. And that’s exactly what we do in the (ngSubmit)
output.
We bind to the ngSubmit
action of our form by using the syntax: (ngSubmit)="onSubmit(f.value)"
.
(ngSubmit)
- comes fromNgForm
onSubmit()
- will be implemented in our component definition class (below)f.value
-f
is theFormGroup
that we specified above. And.value
will return the key/value pairs of thisFormGroup
Put it all together and that line says “when I submit the form, call onSubmit
on my component instance and pass the value of the form as the argument”.
input
& NgModel
Our input
tag has a few things we should touch on before we talk about NgModel
:
<form #f="ngForm"
(ngSubmit)="onSubmit(f.value)"
class="ui form">
<div class="field">
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
name="sku" ngModel>
</div>
class="ui form"
andclass="field"
- these classes are totally optional. They come from the CSS framework Semantic UI. I’ve added them in some of our examples just to give them a nice coat of CSS but they’re not part of Angular.- The
label
“for
” attribute and theinput
“id
” attribute are to match, as per W3C standard - We set a
placeholder
of “SKU”, which is just a hint to the user for what thisinput
should say when it is blank
The NgModel
directive specifies a selector
of ngModel
. This means we can attach it to our input
tag by adding this sort of attribute: ngModel="whatever"
. In this case, we specify ngModel
with no attribute value.
There are a couple of different ways to specify ngModel
in your templates and this is the first. When we use ngModel
with no attribute value we are specifying:
- a one-way data binding
- we want to create a
FormControl
on this form with the namesku
(because of thename
attribute on theinput
tag)
NgModel
creates a new FormControl
that is automatically added to the parent FormGroup
(in this case, on the form) and then binds a DOM element to that new FormControl
. That is, it sets up an association between the input
tag in our view and the FormControl
and the association is matched by a name, in this case "sku"
.
Simple SKU Form: Component Definition Class
Now let’s look at our class definition:
export class DemoFormSkuComponent implements OnInit {
constructor() { }
ngOnInit() {
}
onSubmit(form: any): void {
console.log('you submitted value:', form);
}
}
Here our class defines one function: onSubmit
. This is the function that is called when the form is submitted. For now, we’ll just console.log
out the value that is passed in.
Try it out!
Putting it all together, here’s what our code listing looks like:
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-demo-form-sku',
templateUrl: './demo-form-sku.component.html',
styles: []
})
export class DemoFormSkuComponent implements OnInit {
constructor() { }
ngOnInit() {
}
onSubmit(form: any): void {
console.log('you submitted value:', form);
}
}
and the template:
<div class="ui raised segment">
<h2 class="ui header">Demo Form: Sku</h2>
<form #f="ngForm"
(ngSubmit)="onSubmit(f.value)"
class="ui form">
<div class="field">
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
name="sku" ngModel>
</div>
<button type="submit" class="ui button">Submit</button>
</form>
</div>
If we try this out in our browser, here’s what it looks like:

Using FormBuilder
Building our FormControl
s and FormGroup
s implicitly using ngForm
is convenient, but doesn’t give us a lot of customization options. A more flexible and common way to configure forms is to use a FormBuilder
.
FormBuilder
is an aptly-named helper class that helps us build forms. As you recall, forms are made up of FormControl
s and FormGroup
s and the FormBuilder
helps us make them (you can think of it as a “factory” object).
Let’s add a FormBuilder
to our previous example. Let’s look at:
- how to use the
FormBuilder
in our component definition class - how to use our custom
FormGroup
on aform
in the view
Reactive Forms with FormBuilder
For this component we’re going to be using the formGroup
and formControl
directives which means we need to import the appropriate classes. We start by importing them like so:
import { Component, OnInit } from '@angular/core';
import {
FormBuilder,
FormGroup
} from '@angular/forms';
Using FormBuilder
We inject FormBuilder
by creating an argument in the constructor
of our component class:
import { Component, OnInit } from '@angular/core';
import {
FormBuilder,
FormGroup
} from '@angular/forms';
@Component({
selector: 'app-demo-form-sku-with-builder',
templateUrl: './demo-form-sku-with-builder.component.html',
styles: []
})
export class DemoFormSkuWithBuilderComponent implements OnInit {
myForm: FormGroup;
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['ABC123']
});
}
ngOnInit() {
}
onSubmit(value: string): void {
console.log('you submitted value: ', value);
}
}
During injection an instance of FormBuilder
will be created and we assign it to the fb
variable (in the constructor).
There are two main functions we’ll use on FormBuilder
:
control
- creates a newFormControl
group
- creates a newFormGroup
Notice that we’ve setup a new instance variable called myForm
on this class. (We could have just as easily called it form
, but I want to differentiate between our FormGroup
and the form
we had before.)
myForm
is typed to be a FormGroup
. We create a FormGroup
by calling fb.group()
. .group
takes an object of key-value pairs that specify the FormControl
s in this group.
In this case, we’re setting up one control sku
, and the value is ["ABC123"]
- this says that the default value of this control is "ABC123"
. (You’ll notice that is an array. That’s because we’ll be adding more configuration options there later.)
Now that we have myForm
we need to use that in the view (i.e. we need to bind it to our form
element).
Using myForm
in the view
We want to change our <form>
to use myForm
. If you recall, in the last section we said that ngForm
is applied for us automatically when we use FormsModule
. We also mentioned that ngForm
creates its own FormGroup
. Well, in this case, we don’t want to use an outside FormGroup
. Instead we want to use our instance variable myForm
, which we created with our FormBuilder
. How can we do that?
Angular provides another directive that we use when we have an existing FormGroup
: it’s called formGroup
and we use it like this:
<h2 class="ui header">Demo Form: Sku with Builder</h2>
<form [formGroup]="myForm"
Here we’re telling Angular that we want to use myForm
as the FormGroup
for this form.
We also need to change onSubmit
to use myForm
instead of f
, because now it is myForm
that has our configuration and values.
There’s one last thing we need to do to make this work: bind our FormControl
to the input
tag.
When we want to bind an existing FormControl
to an input
we use formControl
:
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
[formControl]="myForm.controls['sku']">
Here we are instructing the formControl
directive to look at myForm.controls
and use the existing sku
FormControl
for this input
.
Try it out!
Here’s what it looks like all together:
import { Component, OnInit } from '@angular/core';
import {
FormBuilder,
FormGroup
} from '@angular/forms';
@Component({
selector: 'app-demo-form-sku-with-builder',
templateUrl: './demo-form-sku-with-builder.component.html',
styles: []
})
export class DemoFormSkuWithBuilderComponent implements OnInit {
myForm: FormGroup;
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['ABC123']
});
}
ngOnInit() {
}
onSubmit(value: string): void {
console.log('you submitted value: ', value);
}
}
and the template:
<div class="ui raised segment">
<h2 class="ui header">Demo Form: Sku with Builder</h2>
<form [formGroup]="myForm"
(ngSubmit)="onSubmit(myForm.value)"
class="ui form">
<div class="field">
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
[formControl]="myForm.controls['sku']">
</div>
<button type="submit" class="ui button">Submit</button>
</form>
</div>
Remember:
To create a new FormGroup
and FormControl
s implicitly use:
ngForm
andngModel
But to bind to an existing FormGroup
and FormControl
s use:
formGroup
andformControl
Adding Validations
Our users aren’t always going to enter data in exactly the right format. If someone enters data in the wrong format, we want to give them feedback and not allow the form to be submitted. For this we use validators.
Validators are provided by the Validators
module and the simplest validator is Validators.required
which simply says that the designated field is required or else the FormControl
will be considered invalid.
To use validators we need to do two things:
- Assign a validator to the
FormControl
object - Check the status of the validator in the view and take action accordingly
To assign a validator to a FormControl
object we simply pass it as the second argument to our FormControl
constructor:
let control = new FormControl('sku', Validators.required);
Or in our case, because we’re using FormBuilder
we will use the following syntax:
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['', Validators.required]
});
this.sku = this.myForm.controls['sku'];
}
Now we need to use our validation in the view. There are two ways we can access the validation value in the view:
- We can explicitly assign the
FormControl
sku
to an instance variable of the class - which is more verbose, but gives us easy access to theFormControl
in the view. - We can lookup the
FormControl
sku
frommyForm
in the view. This requires less work in the component definition class, but is slightly more verbose in the view.
To make this difference clearer, let’s look at this example both ways:
Explicitly setting the sku
FormControl
as an instance variable
Here’s a screenshot of what our form is going to look like with validations:

The most flexible way to deal with individual FormControls
in your view is to set each FormControl
up as an instance variable in your component definition class. Here’s how we could setup sku
in our class:
export class DemoFormWithValidationsExplicitComponent {
myForm: FormGroup;
sku: AbstractControl;
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['', Validators.required]
});
this.sku = this.myForm.controls['sku'];
}
onSubmit(value: string): void {
console.log('you submitted value: ', value);
}
}
Notice that:
- We setup
sku: AbstractControl
at the top of the class and - We assign
this.sku
after we’ve createdmyForm
with theFormBuilder
This is great because it means we can reference sku
anywhere in our component view. The downside is that by doing it this way, we’d have to setup an instance variable for every field in our form. For large forms, this can get pretty verbose.
Now that we have our sku
being validated, I want to look at four different ways we can use it in our view:
- Checking the validity of our whole form and displaying a message
- Checking the validity of our individual field and displaying a message
- Checking the validity of our individual field and coloring the field red if it’s invalid
- Checking the validity of our individual field on a particular requirement and displaying a message
Form message
We can check the validity of our whole form by looking at myForm.valid
:
<div *ngIf="!myForm.valid"
Remember, myForm
is a FormGroup
and a FormGroup
is valid if all of the children FormControl
s are also valid.
Field message
We can also display a message for the specific field if that field’s FormControl
is invalid:
[formControl]="sku">
<div *ngIf="!sku.valid"
class="ui error message">SKU is invalid</div>
<div *ngIf="sku.hasError('required')"
Field coloring
I’m using the Semantic UI CSS Framework’s CSS class .error
, which means if I add the class error
to the <div class= "field">
it will show the input tag with a red border.
To do this, we can use the property syntax to set conditional classes:
<div class="field"
[class.error]="!sku.valid && sku.touched">
Notice here that we have two conditions for setting the .error
class: We’re checking for !sku.valid
and sku.touched
. The idea here is that we only want to show the error state if the user has tried editing the form (“touched” it) and it’s now invalid.
To try this out, enter some data into the input
tag and then delete the contents of the field.
Specific validation
A form field can be invalid for many reasons. We often want to show a different message depending on the reason for a failed validation.
To look up a specific validation failure we use the hasError
method:
<div *ngIf="sku.hasError('required')"
class="ui error message">SKU is required</div>
Note that hasError
is defined on both FormControl
and FormGroup
. This means you can pass a second argument of path
to lookup a specific field from FormGroup
. For example, we could have written the previous example as:
<div *ngIf="myForm.hasError('required', 'sku')"
class="error">SKU is required</div>
Putting it together
Here’s the full code listing of our form with validations with the FormControl
set as an instance variable:
import { Component } from '@angular/core';
import {
FormBuilder,
FormGroup,
Validators,
AbstractControl
} from '@angular/forms';
@Component({
selector: 'app-demo-form-with-validations-explicit',
templateUrl: './demo-form-with-validations-explicit.component.html',
styles: []
})
export class DemoFormWithValidationsExplicitComponent {
myForm: FormGroup;
sku: AbstractControl;
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['', Validators.required]
});
this.sku = this.myForm.controls['sku'];
}
onSubmit(value: string): void {
console.log('you submitted value: ', value);
}
}
And the template:
<div class="ui raised segment">
<h2 class="ui header">Demo Form: with validations (explicit)</h2>
<form [formGroup]="myForm"
(ngSubmit)="onSubmit(myForm.value)"
class="ui form"
[class.error]="!myForm.valid && myForm.touched">
<div class="field"
[class.error]="!sku.valid && sku.touched">
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
[formControl]="sku">
<div *ngIf="!sku.valid"
class="ui error message">SKU is invalid</div>
<div *ngIf="sku.hasError('required')"
class="ui error message">SKU is required</div>
</div>
<div *ngIf="!myForm.valid"
class="ui error message">Form is invalid</div>
<button type="submit" class="ui button">Submit</button>
</form>
</div>
Removing the sku
instance variable
In the example above we set sku: AbstractControl
as an instance variable. We often won’t want to create an instance variable for each AbstractControl
, so how would we reference this FormControl
in our view without an instance variable?
Instead we can use the myForm.controls
property as in:
<label for="skuInput">SKU</label>
<input type="text"
id="skuInput"
placeholder="SKU"
[formControl]="myForm.controls['sku']">
<div *ngIf="!myForm.controls['sku'].valid"
class="ui error message">SKU is invalid</div>
<div *ngIf="myForm.controls['sku'].hasError('required')"
In this way we can access the sku
control without being forced to explicitly add it as an instance variable on the component class.
Custom Validations
We often are going to want to write our own custom validations. Let’s take a look at how to do that.
To see how validators are implemented, let’s look at Validators.required
from the Angular core source:
export class Validators {
static required(c: FormControl): StringMap<string, boolean> {
return isBlank(c.value) || c.value == "" ? {"required": true} : null;
}
A validator:
- Takes a FormControl
as its input and
- Returns a StringMap<string, boolean>
where the key is “error code” and the value is true
if it fails
Writing the Validator
Let’s say we have specific requirements for our sku
. For example, say our sku
needs to begin with 123
. We could write a validator like so:
function skuValidator(control: FormControl): { [s: string]: boolean } {
if (!control.value.match(/^123/)) {
return {invalidSku: true};
}
}
This validator will return an error code invalidSku
if the input (the control.value
) does not begin with 123
.
Assigning the Validator to the FormControl
Now we need to add the validator to our FormControl
. However, there’s one small problem: we already have a validator on sku
. How can we add multiple validators to a single field?
For that, we use Validators.compose
:
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['', Validators.compose([
Validators.required, skuValidator])]
});
Validators.compose
wraps our two validators and lets us assign them both to the FormControl
. The FormControl
is not valid
unless both validations are valid.
Now we can use our new validator in the view:
<div *ngIf="sku.hasError('invalidSku')"
class="ui error message">SKU must begin with <span>123</span></div>
If you run the sample code, one neat thing you’ll notice is that if you type something in to the field, the required
validation will be fulfilled, but the invalidSku
validation may not. This is great - it means we can partially-validate our fields and show the appropriate messages.
Watching For Changes
So far we’ve only extracted the value from our form by calling onSubmit
when the form is submitted. But often we want to watch for any value changes on a control.
Both FormGroup
and FormControl
have an EventEmitter
that we can use to observe changes.
To watch for changes on a control we:
- get access to the
EventEmitter
by callingcontrol.valueChanges
. Then we - add an observer using the
.subscribe
method
Here’s an example:
constructor(fb: FormBuilder) {
this.myForm = fb.group({
'sku': ['', Validators.required]
});
this.sku = this.myForm.controls['sku'];
this.sku.valueChanges.subscribe(
(value: string) => {
console.log('sku changed to:', value);
}
);
this.myForm.valueChanges.subscribe(
(form: any) => {
console.log('form changed to:', form);
}
);
}
Here we’re observing two separate events: changes on the sku field and changes on the form as a whole.
The observable that we pass in is an object with a single key: next
(there are other keys you can pass in, but we’re not going to worry about those now). next
is the function we want to call with the new value whenever the value changes.
If we type ‘kj
’ into the text box we will see in our console:
sku changed to: k
form changed to: Object {sku: "k"}
sku changed to: kj
form changed to: Object {sku: "kj"}
As you can see each keystroke causes the control to change, so our observable is triggered. When we observe the individual FormControl
we receive a value (e.g. kj
), but when we observe the whole form, we get an object of key-value pairs (e.g. {sku: "kj"}
).
ngModel
NgModel
is a special directive: it binds a model to a form. ngModel
is special in that it mimics two-way data binding.
Two-way data binding is almost always more complicated and difficult to reason about vs. one-way data binding. Angular is built to generally have data flow one-way: top-down. However, when it comes to forms, there are times where it is easier to opt-in to a two-way bind.
Let’s change our form a little bit and say we want to input productName
. We’re going to use ngModel
to keep the component instance variable in sync with the view.
First, here’s our component definition class:
export class DemoFormNgModelComponent {
productName: string;
constructor() {
this.productName = "ng-book: The Complete Guide to Angular"
}
onSubmit(value: string): void {
console.log('you submitted value: ', value);
}
}
Notice that we’re simply storing productName: string
as an instance variable.
Next, let’s use ngModel
on our input
tag:
<label for="productNameInput">Product Name</label>
<input type="text"
id="productNameInput"
placeholder="Product Name"
name="productName"
[(ngModel)]="productName">
Now notice something - the syntax for ngModel
is funny: we are using both brackets and parentheses around the ngModel
attribute! The idea this is intended to invoke is that we’re using both the input []
brackets and the output ()
parentheses. It’s an indication of the two-way bind.
Last, let’s display our productName
value in the view:
<div class="ui info message">
The product name is: {{productName}}
</div>
Here’s what it looks like:

Easy!
Wrapping Up
Forms have a lot of moving pieces, but Angular makes it fairly straightforward. Once you get a handle on how to use FormGroup
s, FormControl
s, and Validation
s, it’s pretty easy going from there!
ng-book: The Complete Guide to Angular. If you'd like to become an Angular expert in a few hours, then checkout
ng-book.
Pingback: The Ultimate Guide to Forms in Angular 2 « ng-book.com – blog - angular.org.il()