@ -7,7 +7,7 @@ import { AppComponent } from './app.component';
|
||||
import { HeroFormTemplateComponent } from './template/hero-form-template.component';
|
||||
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
||||
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||
|
||||
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -19,7 +19,8 @@ import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||
AppComponent,
|
||||
HeroFormTemplateComponent,
|
||||
HeroFormReactiveComponent,
|
||||
ForbiddenValidatorDirective
|
||||
ForbiddenValidatorDirective,
|
||||
IdentityRevealedValidatorDirective
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -0,0 +1,42 @@
|
||||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
templateUrl: './hero-form-reactive.component.html',
|
||||
styleUrls: ['./hero-form-reactive.component.css'],
|
||||
})
|
||||
export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||
|
||||
heroForm: FormGroup;
|
||||
|
||||
// #docregion form-group
|
||||
ngOnInit(): void {
|
||||
// #docregion custom-validator
|
||||
this.heroForm = new FormGroup({
|
||||
'name': new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
// #enddocregion custom-validator
|
||||
|
||||
}
|
||||
|
||||
get name() { return this.heroForm.get('name'); }
|
||||
|
||||
get power() { return this.heroForm.get('power'); }
|
||||
// #enddocregion form-group
|
||||
}
|
||||
// #enddocregion
|
@ -0,0 +1,5 @@
|
||||
/* #docregion cross-validation-error-css */
|
||||
.cross-validation-error input {
|
||||
border-left: 5px solid red;
|
||||
}
|
||||
/* #enddocregion cross-validation-error-css */
|
@ -3,38 +3,67 @@
|
||||
|
||||
<h1>Reactive Form</h1>
|
||||
|
||||
<!-- #docregion cross-validation -->
|
||||
<form [formGroup]="heroForm" #formDir="ngForm">
|
||||
<!-- #enddocregion cross-validation -->
|
||||
|
||||
<div [hidden]="formDir.submitted">
|
||||
|
||||
<div class="form-group">
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class -->
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||
<!-- #enddocregion cross-validation-error-class -->
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<div class="form-group">
|
||||
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<input id="name" class="form-control"
|
||||
formControlName="name" required >
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class-->
|
||||
<input id="name" class="form-control"
|
||||
formControlName="name" required >
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<!-- #enddocregion cross-validation-error-class-->
|
||||
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
<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 Bob.
|
||||
<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 Bob.
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
formControlName="alterEgo" >
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class-->
|
||||
<input id="alterEgo" class="form-control"
|
||||
formControlName="alterEgo" >
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<!-- #enddocregion cross-validation-error-class -->
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
<!-- #docregion cross-validation -->
|
||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||
Name cannot match alter ego.
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<!-- #enddocregion cross-validation-error-message -->
|
||||
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class-->
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<!-- #enddocregion cross-validation-error-class -->
|
||||
|
||||
<div class="form-group">
|
||||
<label for="power">Hero Power</label>
|
||||
@ -53,7 +82,9 @@
|
||||
<button type="button" class="btn btn-default"
|
||||
(click)="formDir.resetForm({})">Reset</button>
|
||||
</div>
|
||||
<!-- #docregion cross-validation -->
|
||||
</form>
|
||||
<!-- #enddocregion cross-validation -->
|
||||
|
||||
<div class="submitted-message" *ngIf="formDir.submitted">
|
||||
<p>You've submitted your hero, {{ heroForm.value.name }}!</p>
|
||||
|
@ -1,40 +1,44 @@
|
||||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
|
||||
|
||||
// #docregion cross-validation-component
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
templateUrl: './hero-form-reactive.component.html'
|
||||
templateUrl: './hero-form-reactive.component.html',
|
||||
styleUrls: ['./hero-form-reactive.component.css'],
|
||||
})
|
||||
export class HeroFormReactiveComponent implements OnInit {
|
||||
// #enddocregion cross-validation-component
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
|
||||
|
||||
// #docregion cross-validation-component
|
||||
heroForm: FormGroup;
|
||||
|
||||
// #docregion form-group
|
||||
// #docregion cross-validation-register
|
||||
ngOnInit(): void {
|
||||
// #docregion custom-validator
|
||||
this.heroForm = new FormGroup({
|
||||
'name': new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
// #enddocregion custom-validator
|
||||
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
||||
}
|
||||
// #enddocregion cross-validation-register
|
||||
// #enddocregion cross-validation-component
|
||||
|
||||
get name() { return this.heroForm.get('name'); }
|
||||
|
||||
get power() { return this.heroForm.get('power'); }
|
||||
// #enddocregion form-group
|
||||
// #docregion cross-validation-component
|
||||
}
|
||||
// #enddocregion
|
||||
// #enddocregion cross-validation-component
|
||||
|
@ -0,0 +1,28 @@
|
||||
// #docregion
|
||||
import { Directive } from '@angular/core';
|
||||
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
|
||||
|
||||
// #docregion cross-validation-directive-with-validator
|
||||
// #docregion cross-validation-validator
|
||||
/** A hero's name can't match the hero's alter ego */
|
||||
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
|
||||
const name = control.get('name');
|
||||
const alterEgo = control.get('alterEgo');
|
||||
|
||||
return name && alterEgo && name.value !== alterEgo.value ? null : { 'identityRevealed': { value: true } };
|
||||
};
|
||||
// #enddocregion cross-validation-validator
|
||||
|
||||
// #docregion cross-validation-directive
|
||||
@Directive({
|
||||
selector: '[appIdentityRevealed]',
|
||||
providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
|
||||
})
|
||||
export class IdentityRevealedValidatorDirective implements Validator {
|
||||
validate(control: AbstractControl): ValidationErrors {
|
||||
return identityRevealedValidator(control)
|
||||
}
|
||||
}
|
||||
// #enddocregion cross-validation-directive
|
||||
// #enddocregion cross-validation-directive-with-validator
|
||||
|
@ -0,0 +1,4 @@
|
||||
/* #docregion */
|
||||
.cross-validation-error input {
|
||||
border-left: 5px solid red;
|
||||
}
|
@ -2,42 +2,71 @@
|
||||
<div class="container">
|
||||
|
||||
<h1>Template-Driven Form</h1>
|
||||
<!-- #docregion form-tag-->
|
||||
<form #heroForm="ngForm">
|
||||
<!-- #enddocregion form-tag-->
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-register-validator -->
|
||||
<form #heroForm="ngForm" appIdentityRevealed>
|
||||
<!-- #enddocregion cross-validation-register-validator -->
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<div [hidden]="heroForm.submitted">
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class -->
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||
<!-- #enddocregion cross-validation-error-class -->
|
||||
<!-- #enddocregion cross-validation -->
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion cross-validation-error-class-->
|
||||
<!-- #enddocregion cross-validation-->
|
||||
<!-- #enddocregion name-input -->
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion name-input -->
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
|
||||
<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 Bob.
|
||||
</div>
|
||||
|
||||
<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 Bob.
|
||||
</div>
|
||||
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-class -->
|
||||
<input id="alterEgo" class="form-control"
|
||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||
<!-- #enddocregion cross-validation-error-class -->
|
||||
<!-- #enddocregion cross-validation -->
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation -->
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||
Name cannot match alter ego.
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation-error-message -->
|
||||
<!-- #enddocregion cross-validation -->
|
||||
|
||||
<!-- #docregion cross-validation-->
|
||||
<!-- #docregion cross-validation-error-class -->
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation-error-class -->
|
||||
<!-- #enddocregion cross-validation -->
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="power">Hero Power</label>
|
||||
@ -61,6 +90,9 @@
|
||||
<p>You've submitted your hero, {{ heroForm.value.name }}!</p>
|
||||
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation -->
|
||||
</form>
|
||||
<!-- #enddocregion cross-validation -->
|
||||
|
||||
</div>
|
||||
|
@ -3,9 +3,11 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion component
|
||||
@Component({
|
||||
selector: 'app-hero-form-template',
|
||||
templateUrl: './hero-form-template.component.html'
|
||||
templateUrl: './hero-form-template.component.html',
|
||||
styleUrls: ['./hero-form-template.component.css'],
|
||||
})
|
||||
export class HeroFormTemplateComponent {
|
||||
|
||||
@ -14,3 +16,4 @@ export class HeroFormTemplateComponent {
|
||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||
|
||||
}
|
||||
// #enddocregion
|
||||
|
Reference in New Issue
Block a user