Author avatar

Yallaling Goudar

Validating Angular Template Driven Forms

Yallaling Goudar

  • Jul 30, 2019
  • 8 Min read
  • 415 Views
  • Jul 30, 2019
  • 8 Min read
  • 415 Views
Languages Frameworks and Tools
Angular

Introduction

In this guide, we will be exploring the Angular template-driven form validation. Form validation, in general, will help us to improve the overall data quality by validating user input for accuracy and completeness. In this guide, we will see how to validate the user input in the UI and display useful validation messages using template-driven form.

Validation in Template-driven Form

In template-driven form, we will be adding the same attributes as we would with native HTML form validation. Angular uses directives to match these attributes with validator functions in the framework. Whenever the value of a form control changes, Angular runs validation and generates either a list of validation errors, which results in an INVALID status or null, which results in a VALID status. We can check the control’s state by exporting ngModel to a local template variable. Let’s consider the below example, which will explain how the exported NgModel to a variable called name.

Filename: employee-form-template.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input id="employee" name=" employeename" class="form-control"
      required maxlength="8" appForbiddenName="yash"
      [(ngModel)]="emploee.name" #employee="ngModel" >
 
<div *ngIf=" employeename.invalid && (employeename.dirty || employeename.touched)"
    class="alert alert-danger">
 
  <div *ngIf=" employeename.errors.required">
    EmployeeName is required.
  </div>
  <div *ngIf=" employeename.errors.maxlength">
    EmployeeName must be at last 8 characters long.
  </div>
  <div *ngIf=" employeename.errors.forbiddenName">
    EmployeeName cannot be Yash.
  </div>
 
</div>
typescript

In the above example, the input tag contains the validation attributes like required and minlength. Note that it also carries a custom validator directive forbiddenEmployeeName. #employeename="ngModel" exports NgModel into a local variable called employeename. NgModel contains many properties of underlying FormControl instance. By using this, we will be able to check the control states such as valid and dirty. The *ngIf on the div tag validates if the employeename is invalid and if the control is either dirty or touched.

Adding Custom Validators to Forms

In some cases, we may want to create the custom validators based on our needs, as the built-in validators won’t always match the exact needs of your application. Let’s consider an example where we are creating the forbiddenEmployeeNameValidator custom validator.

Filename: forbidden-employeename.directive.ts

1
2
3
4
5
6
export function forbiddenEmployeeNameValidator(employeenameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} | null => {
    const forbidden = employeenameRe.test(control.value);
    return forbidden ? {'forbiddenEmployeeName': {value: control.value}} : null;
  };
}
typescript

The function here is actually a factory that takes a regular expression to detect a specific forbidden employeename and returns a validator function. In the above example the forbidden employeename is “yash”. The validator will reject any employee name containing “yash”.

The forbiddenEmployeeNameValidator factory returns the configured validator function. That function takes an Angular control object and returns either null, if the control value is valid, or a validation error object. The validation error object typically has a property whose name is the validation key, 'forbiddenEmployeeName', and whose value is an arbitrary dictionary of values that you could insert into an error message, {employeename}.

Custom async validators are similar to sync validators, but they must instead return a Promise or Observable that later emits null or a validation error object. In the case of an Observable, the Observable must complete, at which point the form uses the last value emitted for validation.

Adding Custom Validators to Template-driven Forms

In template-driven forms, we can add custom validators by adding a directive to the template. We don’t have the direct access to the FormControl instance, so we can’t pass the validator in like you can for reactive forms. The corresponding ForbiddenValidatorDirective serves as a wrapper around the forbiddenEmployeeNameValidator. The corresponding ForbiddenValidatorDirective serves as a wrapper around the forbiddenEmployeeNameValidator. When we register the directive with Angular then Angular recognizes the directive’s role in the validation process because the directive registers itself with the NG_VALIDATORS provider, a provider with an extensible collection of validators. The directive class then implements the Validator interface, so that it can easily integrate with Angular forms.

Filename: forbidden-employeename.directive.ts

1
2
3
4
5
6
7
8
9
10
11
12
@Directive({
  selector: '[appForbiddenEmployeeName]',
  providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
})
export class ForbiddenValidatorDirective implements Validator {
  @Input('appForbiddenEmployeeName') forbiddenEmployeeName: string;
 
  validate(control: AbstractControl): {[key: string]: any} | null {
    return this.forbiddenEmployeeName ? forbiddenEmployeeNameValidator(new RegExp(this.forbiddenEmployeeName, 'i'))(control)
                          	: null;
  }
}
typescript

Control Status CSS Classes in Angular

Angular automatically contains many control properties for the form control element as CSS classes. We can use these classes to style form control elements according to the state of the form. The following classes are currently supported in the CSS:

  • .ng-valid
  • .ng-invalid
  • .ng-pending
  • .ng-pristine
  • .ng-dirty
  • .ng-untouched
  • .ng-touched

Below are the examples of the styles that we can use in our applications like .ng-valid and .ng-invalid.

Filename: forms.css

1
2
3
4
5
6
7
.ng-valid[required], .ng-valid.required  {
  border-left: 6px solid green;
}
 
.ng-invalid:not(form)  {
  border-left: 6px solid red;
}
css

Cross Field Validation in Template-driven Form in Angular

In this section, we will see how to perform cross field validation in the template-driven form in Angular. Now, let’s create a directive that will wrap the validator function. We provide it as the validator using the NG_VALIDATORS token.

Filename: cross-field.directive.ts

1
2
3
4
5
6
7
8
9
@Directive({
  selector: '[appCrossField]',
  providers: [{ provide: NG_VALIDATORS, useExisting: CrossFieldValidatorDirective, multi: true }]
})
export class CrossFieldValidatorDirective implements Validator {
  validate(control: AbstractControl): ValidationErrors {
    return crossFieldValidator(control)
  }
}
typescript

Now, let’s add the directive to the HTML template. Since the validator must be registered at the highest level in the form, we put the directive on the form tag.

Filename: employee-form-template.component.html

1
<form #employeeForm="ngForm" appCrossField>
html

When the form is invalid we will show an appropriate error message, as shown in the below example.

Filename: employee-form-template.component.html

1
2
3
<div *ngIf="employeeForm.errors?.crossField && (employeeForm.touched || employeeForm.dirty)" class="cross-validation-error-message alert alert-danger">
    Employee Name cannot match.
</div>
html

Here we have to check if the cross-validation error was returned by the crossField validator and if the user is yet to interact with the form.

Conclusion

In this guide, we have explored template-driven form validations in Angular. We have also seen how we can build or create a custom template-driven form validation and use it in our application. You can learn more about Angular Template Driven Forms in my guide Creating Template Driven Forms in Angular.

2