The simplest form behavior imaginable would be the validation of a single input field. Most of the time, utilizing <form>
tags and going through the rest of the boilerplate is good practice, but for the purpose of checking a single input, it's preferable to distill this down to the bare minimum required in order to use input checking.
The code, links, and a live example related to this recipe are available at .
Suppose the following is your initial setup:
[app/article-editor.component.ts] import {Component} from '@angular/core'; @Component({ selector: 'article-editor', template: ` <p>Article title (required):</p> <input required> <button>Save</button> <h1>{{title}}</h1> ` }) export class ArticleEditorComponent { title:string; }
Your goal is to change this in a way that clicking the save button will validate the input and update the title
member only if it is valid.
The most elemental component of Angular forms is the FormControl
object. In order to be able to assess the state of the field, you first need to instantiate this object inside the component and associate it with the field using the formControl
directive. FormControl
lives inside ReactiveFormsModule
. Add it as a module import:
[app/app.module.ts] import {NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; import {ReactiveFormsModule} from '@angular/forms'; import {ArticleEditorComponent} from './article-editor.component'; @NgModule({ imports: [ BrowserModule, ReactiveFormsModule ], declarations: [ ArticleEditorComponent ], bootstrap: [ ArticleEditorComponent ] }) export class AppModule {}
With this, you can use FormControl
inside ArticleEditorComponent
. Instantiate FormControl
inside the component and bind the input element to it using the formControl
directive:
[app/article-editor.component.ts] import {Component} from '@angular/core'; import {FormControl} from '@angular/forms'; @Component({ selector: 'article-editor', template: ` <p>Article title (required):</p> <input [formControl]="titleControl" required> <button>Save</button> <h1>{{title}}</h1> ` }) export class ArticleEditorComponent { title:string; titleControl:FormControl = new FormControl(); }
Now that you have created a FormControl
object and associated it with an input field, you will be able to use its validation API to check the state of the field. All that is left is to use it inside the submit click handler:
[app/article-editor.component.ts] import {Component} from '@angular/core'; import {FormControl} from '@angular/forms'; @Component({ selector: 'article-editor', template: ` <p>Article title (required):</p> <input [formControl]="titleControl" required> <button (click)="submitTitle()">Save</button> <h1>{{title}}</h1> ` }) export class ArticleEditorComponent { title:string; titleControl:FormControl = new FormControl(); submitTitle():void { if(this.titleControl.valid) { this.title = this.titleControl.value; } else { alert("Title required"); } } }
With this, the submit click handler will be able to check the input's validation state and value with the same object.
The formControl
directive serves only to bind an existing FormControl
object to a DOM element. The FormControl
object that you instantiate inside the component constructor can either utilize validation attributes inside an HTML tag (as is done in this example), or accept Angular validators when initialized; or, it can do both.
It's extremely important to note that just because the FormControl
object is instantiated, it does not mean that it is able to validate the input immediately.
Without an initialized value, an empty input field will begin its life with a value of null
, which in the presence of a required
attribute is of course invalid. However, in this example, if you were to check whether the FormControl
object becomes valid immediately after you instantiate it in the constructor, the FormControl
object would dutifully inform you that the state is valid since it has not been bound to the DOM element yet, and therefore, no validations are being violated. Since the input element's formControl
binding will not occur until the component template becomes part of the actual DOM, you will not be able to check the input state until the binding is complete or inside the ngAfterContentChecked
life cycle hook. Note that this pertains to the example under consideration.
Once the formControl
directive completes the binding, the FormControl
object will exist as an input wrapper, allowing you to use its valid
and value
members.
This recipe uses ReactiveFormsModule
, which is simpler to understand since all of the setup is explicit. When you use FormsModule
instead, you discover that a lot of what is accomplished in this recipe could be done automatically for you, such as the instantiation and binding of FormControl
objects. It also revolves around the presence of a <form>
tag, which is the de facto top-level FormControl
container. This recipe serves to demonstrate one of the simplest forms of Angular form behavior.
As mentioned in this recipe, validation definitions can come from two places. Here, you used a standardized HTML tag attribute that Angular recognizes and automatically incorporates into the FormControl
validation specification. You could have just as easily elected to utilize an Angular Validator
to accomplish the same task instead. This can be accomplished by importing Angular's default Validators
and initializing the FormControl
object with the required
validator:
[app/article-editor.component.ts] import {Component} from '@angular/core'; import {FormControl, Validators} from '@angular/forms'; @Component({ selector: 'article-editor', template: ` <p>Article title (required):</p> <input [formControl]="titleControl"> <button (click)="submitTitle()">Save</button> <h1>{{title}}</h1> ` }) export class ArticleEditorComponent { title:string; // First argument is the default input value titleControl:FormControl = new FormControl(null, Validators.required); submitTitle():void { if(this.titleControl.valid) { this.title = this.titleControl.value; } else { alert("Title required"); } } }
As you might suspect, there is no reason a FormControl
must be bound to a DOM element. FormControl
is an elemental piece of form logic that acts as an atomic piece of stateful information, whether or not this information is derived from <input>
. Say you wanted to add a FormControl
that would prevent quick form submission by only becoming valid after 10 seconds. You could explicitly create a FormControl
object that would tie into the combined form validation but would not be associated with a DOM element.