Angular Forms Validation: Part I - Single control validation
Tags: angular, validation, reactiveforms, forms
Published: 2020-01-17 08:12:52

Angular offers powerful tools for managing forms. In this series of posts, I will discuss various Reactive Forms Validation examples, ranging from the simplest to more advanced ones.

In the first part of the series, we will focus on __single__ form control validation. You can apply zero or any number of synchronous and asynchronous validators to a single control.
 
### Let's start from the simplest possible example 

A FormControl without validation, along with the Angular component markup to display this FormControl:
```typescript
export class SingleControlComponent {
  readonly emailControl = new FormControl();
}
```
```html
<label for="email">Email</label>
<input name="email" type="text" [formControl]="emailControl">
```
Running this example will yield the following results in the browser:
![Sample control](https://thepracticaldev.s3.amazonaws.com/i/2gxmi03rmharha5flzeu.png)

### Adding synchronous validation to the FormControl 

We are going to use [validators](https://angular.io/api/forms/Validators) provided by the Angular framework. `Validators.required` - requires to have a non-empty value. `Validators.email` - checking email against the pattern which is based on the definition of a valid email address in the [WHATWG HTML specification](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with some enhancements to incorporate more RFC rules.
```typescript
export class SingleControlComponent {
  readonly emailControl = new FormControl(null, [Validators.required, Validators.email]);
}
```
As the next step, we need to handle validation results in the control markup. The FormControl API is helpful here. The 'hasError' method checks for the presence of an error code ('required', 'email', etc.) in the control's errors collection.

```html
<label for="email">Email</label>
<input name="email" type="text" [formControl]="emailControl">
<div class="errors">
  <span *ngIf="emailControl.hasError('required')">Email is Required</span> 
  <span *ngIf="emailControl.hasError('email')">Email is mailformed</span>
</div>
```
After making these changes, our control should work as shown in the following recording:
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/e2wy76tecui2ygj610fo.gif)

### Implement asynchronous validator. 
The next step will be implementing some asynchronous validation. Let's create an async validator that simulates checking for email presence on the server.
```typescript
function emailMustNotBeUsed(control: AbstractControl): Observable<ValidationErrors | null> {
  return control.value === 'used@email.com' 
      ? of({'email-is-used': 'Email was used'}).pipe(delay(2000))
      : of(null).pipe(delay(2000));   
}
```
We're delaying the execution result to simulate a call to a remote resource. Now, we will add the async validator to the FormControl.
```typescript
export class SingleControlComponent {
  readonly emailControl = new FormControl(null, 
    // Sync validators
    [Validators.required, Validators.email],
    // Async validator
    emailMustNotBeUsed);
}
```
Handling validator results in the component markup by adding the following element:
```html
...
<span *ngIf="emailControl.hasError('email-is-used')">{{emailControl.getError('email-is-used')}}</span> 
...
```
_Note: Error message from the validator is shown at the UI by using `getError` method. It can be helpfull if async validator return specific error message (received from the server for example)._

The results of all our changes will be as shown in the recording below:
![The Results](https://thepracticaldev.s3.amazonaws.com/i/blhzn7h0oflvn603jhwd.gif)

### Validators execution order
The order of execution is guaranteed. First, all synchronous validators are executed in the order of declaration. Once synchronous validators return results and there are no errors, asynchronous validators are initiated. Asynchronous validators are __initiated__ in the order of declaration, but the order of execution results is not guaranteed. Let's take a look at the code.
```typescript
export class SingleControlComponent {
  readonly emailControl = new FormControl(null, 
    // Sync validators
    [Validators.required, Validators.email],
    // Async validators
    [emailMustNotBeUsed, emailIsForbidden]);
}
```

The implementation of asynchronous validators used for this form control is presented below:
```typescript
function emailMustNotBeUsed(control: AbstractControl): Observable<ValidationErrors | null> {
  console.log('First async validator stars executing');
  const resutl$ = control.value === 'used@email.com' 
      ? of({'email-is-used': 'Email was used'}).pipe(delay(2000))
      : of(null).pipe(delay(2000));
  return resutl$.pipe(finalize(() => console.log('First async validator completed')));    
}

function emailIsForbidden(control: AbstractControl): Observable<ValidationErrors | null> {
  console.log('Second async validator stars executing');
  const resutl$ = control.value === 'forbidden@email.com' 
      ? of({'email-is-forbidden': 'Email was forbidden'}).pipe(delay(1500))
      : of(null).pipe(delay(1500));

  return resutl$.pipe(finalize(() => console.log('Second async validator completed')));    
}
```
The console output of running this sample will be exactly as follows:
> First async validator stars executing
> Second async validator stars executing
> Second async validator completed
> First async validator completed

The first two lines will always be printed in the same order, regardless of the internal async validator implementation. The order of the other two lines depends on the async validation delay.

### Conclusion
Thank you for reading. I hope this information was helpful in some way. All code samples can be found on [Github](https://github.com/musatov/angular-samples/tree/master/form-validators).

There are two other articles available on the topic:
[Angular forms validation. Part II. FormGroup validation.](https://dev.to/musatov/angular-forms-validation-part-ii-formgroup-validation-3g68)
[Angular forms validation. Part III. Async Validators gotchas.](https://dev.to/musatov/angular-forms-validation-part-iii-async-validators-gotchas-5c0o)