Template-DRIVEN Validation
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:
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: