14. Forms: Form Validation

Template-DRIVEN Validation

TechnologyFormTemplateComponent:

import { Component } from '@angular/core';


@Component({
  selector: 'app-technology-form-template',
  template: `
          <div class="container">
              <h1>Template-DRIVEN Form</h1>
              <form #technologyForm="ngForm">
                <div [hidden]="technologyForm.submitted">
                  <div class="form-group">
                      <label for="name">Name</label>
                      <input id="name" name="name" class="form-control"
                             required minlength="4" forbiddenName="React"
                             [(ngModel)]="technology.name" #name="ngModel">
                      <div *ngIf="name.invalid && (name.dirty || name.touched)"
                            class="alert alert-danger">
                      <div *ngIf="name.errors.required">Name is required.</div>
                      <div *ngIf="name.errors.minlength">Name must be at least 4 characters long.</div>
                      <div *ngIf="name.errors.forbiddenName">Name cannot be React.</div>
                      </div>
                  </div>
                  <div class="form-group">
                      <label for="codeName">Code Name</label>
                      <input id="codeName" class="form-control" name="codeName"
                      [(ngModel)]="technology.codeName">
                  </div>
                  <div class="form-group">
                      <label for="power">Technology Power</label>
                      <select id="power" name="power" class="form-control"
                          required [(ngModel)]="technology.power" #power="ngModel">
                          <option *ngFor="let power of powers" [value]="power">{{ power }}</option>
                      </select>
                      <div *ngIf="power.errors && power.touched" class="alert alert-danger">
                          <div *ngIf="power.errors.required">Power is required.</div>
                      </div>
                </div>
                <button type="submit" class="btn btn-default" [disabled]="technologyForm.invalid">
                  Submit
                </button>
                <button type="button" class="btn btn-default" (click)="technologyForm.resetForm({})">
                Reset
                </button>
                </div>
                <div class="submitted-message" *ngIf="technologyForm.submitted">
                <p>You've submitted your technology, {{ technologyForm.value.name }}!</p>
                <button (click)="technologyForm.resetForm({})">Add new Technology</button>
                </div>
              </form>
  `,
  styles: [`
    .ng-valid[required], .ng-valid.required {
      border-left: 5px solid #42A948;
    }

    .ng-invalid:not(form) {
      border-left: 5px solid #a94442;
    }
    `]
})
export class TechnologyFormTemplateComponent {
  powers = ['Data Binding', 'Animations', 'Tooling', 'Beautiful CSS'];
  technology = { name: 'Angular 5', codeName: 'SuperHero', power: this.powers[0] };

}

ForbiddenNameDirective:

import { Directive, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn } from '@angular/forms';

export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
  return (control: AbstractControl): {[key: string]: any} => {
    const forbidden = nameRe.test(control.value);
    return forbidden ? {'forbiddenName': { value: control.value } }: null;
  };
}

@Directive({
  selector: '[forbiddenName]',
  providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
})
export class ForbiddenValidatorDirective implements Validator {
  @Input() forbiddenName: string;

  validate(control: AbstractControl): {[key: string]: any} {
    return this.forbiddenName ?
    forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control) : null;
  }

}

AppComponent:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
        <h1>{{ title }}</h1>
          <div class="left">
              <app-technology-form-template></app-technology-form-template>
          </div>
          <div class="right">
            <img [src]="imageUrl" class="img-round" alt="princess image">
          </div>
  `,
  styles: [`h1 { font-weight: bold; }`]
})
export class AppComponent {
  title = 'Tour of Technologies';
  imageUrl = '../assets/polymer1.jpg-large';

}

Let's have a look at the result in Chromium:

Reactive Form Validation

TechnologyFormReactiveTemplateComponent:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from './forbidden-name.directive';

@Component({
  selector: 'app-technology-reactive-template',
  template: `
  <div class="container">
      <h1>Reactive Form</h1>
      <form [formGroup]="technologyForm" #formDir="ngForm">
        <div [hidden]="formDir.submitted">
          <div class="form-group">
              <label for="name">Name</label>
              <input id="name" class="form-control" formControlName="name"
                     required >

              <div *ngIf="name.invalid && (name.dirty || name.touched)"
                    class="alert alert-danger">
              <div *ngIf="name.errors.required">Name is required.</div>
              <div *ngIf="name.errors.minlength">Name must be at least 4 characters long.</div>
              <div *ngIf="name.errors.forbiddenName">Name cannot be React.</div>
              </div>
          </div>
          <div class="form-group">
              <label for="codeName">Code Name</label>
              <input id="codeName" class="form-control" formControlName="codeName" >
          </div>
          <div class="form-group">
              <label for="power">Technology Power</label>
              <select id="power" class="form-control" formControlName="power"
                  required >
                  <option *ngFor="let power of powers" [value]="power">{{ power }}</option>
              </select>
              <div *ngIf="power.invalid && power.touched" class="alert alert-danger">
                  <div *ngIf="power.errors.required">Power is required.</div>
              </div>
        </div>
        <button type="submit" class="btn btn-default" [disabled]="technologyForm.invalid">
          Submit
        </button>
        <button type="button" class="btn btn-default" (click)="formDir.resetForm({})">
        Reset
        </button>
        </div>
      </form>
      <div class="submitted-message" *ngIf="formDir.submitted">
      <p>You've submitted your technology, {{ technologyForm.value.name }}!</p>
      <button (click)="formDir.resetForm({})">Add new Technology</button>
      </div>
    </div>
  `
})
export class TechnologyFormReactiveTemplateComponent implements OnInit {
  powers = ['Data Binding', 'Animations', 'Tooling', 'Beautiful CSS'];
  technology = { name: 'Angular 5', codeName: 'SuperHero', power: this.powers[0] };

  technologyForm: FormGroup;

  ngOnInit(): void {
    this.technologyForm = new FormGroup({
      'name': new FormControl(this.technology.name, [
        Validators.required,
        Validators.minLength(4),
        forbiddenNameValidator(/React/i) // pass in custom validator here
      ]),
      'codeName': new FormControl(this.technology.codeName),
      'power': new FormControl(this.technology.power, Validators.required)
    });
  }

  get name() { return this.technologyForm.get('name'); }
  get power() { return this.technologyForm.get('power'); }

}

Let's check the results in Chromium:

results matching ""

    No results matching ""