From 002a5afa9880ac3a885a54f9bfcdb994af3ae9e8 Mon Sep 17 00:00:00 2001 From: Tomasz Kula Date: Mon, 7 May 2018 12:41:07 +0200 Subject: [PATCH] docs(aio): add cross field validation example (#23743) PR Close #23743 --- .../form-validation/e2e/src/app.e2e-spec.ts | 34 ++++- .../form-validation/src/app/app.module.ts | 5 +- .../hero-form-reactive.component.1.ts | 42 ++++++ .../reactive/hero-form-reactive.component.css | 5 + .../hero-form-reactive.component.html | 73 +++++++--- .../reactive/hero-form-reactive.component.ts | 26 ++-- .../app/shared/identity-revealed.directive.ts | 28 ++++ .../template/hero-form-template.component.css | 4 + .../hero-form-template.component.html | 88 ++++++++---- .../template/hero-form-template.component.ts | 5 +- .../examples/form-validation/stackblitz.json | 3 +- aio/content/guide/form-validation.md | 128 +++++++++++++++++- 12 files changed, 373 insertions(+), 68 deletions(-) create mode 100644 aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts create mode 100644 aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.css create mode 100644 aio/content/examples/form-validation/src/app/shared/identity-revealed.directive.ts create mode 100644 aio/content/examples/form-validation/src/app/template/hero-form-template.component.css diff --git a/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts b/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts index 8956ace183..e6b8bbf605 100644 --- a/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts +++ b/aio/content/examples/form-validation/e2e/src/app.e2e-spec.ts @@ -16,6 +16,7 @@ describe('Form Validation Tests', function () { tests('Template-Driven Form'); bobTests(); + crossValidationTests(); }); describe('Reactive form', () => { @@ -25,6 +26,7 @@ describe('Form Validation Tests', function () { tests('Reactive Form'); bobTests(); + crossValidationTests(); }); }); @@ -42,7 +44,8 @@ let page: { powerOption: ElementFinder, errorMessages: ElementArrayFinder, heroFormButtons: ElementArrayFinder, - heroSubmitted: ElementFinder + heroSubmitted: ElementFinder, + crossValidationErrorMessage: ElementFinder, }; function getPage(sectionTag: string) { @@ -59,7 +62,8 @@ function getPage(sectionTag: string) { powerOption: section.element(by.css('#power option')), errorMessages: section.all(by.css('div.alert')), heroFormButtons: buttons, - heroSubmitted: section.element(by.css('.submitted-message')) + heroSubmitted: section.element(by.css('.submitted-message')), + crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')), }; } @@ -172,3 +176,29 @@ function bobTests() { expectFormIsValid(); }); } + +function crossValidationTests() { + const emsg = 'Name cannot match alter ego.'; + + it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () { + page.nameInput.clear(); + page.nameInput.sendKeys('Batman'); + + page.alterEgoInput.clear(); + page.alterEgoInput.sendKeys('Batman'); + + expectFormIsInvalid(); + expect(page.crossValidationErrorMessage.getText()).toBe(emsg); + }); + + it('should be ok again with different values', function () { + page.nameInput.clear(); + page.nameInput.sendKeys('Batman'); + + page.alterEgoInput.clear(); + page.alterEgoInput.sendKeys('Superman'); + + expectFormIsValid(); + expect(page.crossValidationErrorMessage.isPresent()).toBe(false); + }); +} diff --git a/aio/content/examples/form-validation/src/app/app.module.ts b/aio/content/examples/form-validation/src/app/app.module.ts index 0a5d1cf455..f25504e6ea 100644 --- a/aio/content/examples/form-validation/src/app/app.module.ts +++ b/aio/content/examples/form-validation/src/app/app.module.ts @@ -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 ] }) diff --git a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts new file mode 100644 index 0000000000..72b5dbeaec --- /dev/null +++ b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.1.ts @@ -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 diff --git a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.css b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.css new file mode 100644 index 0000000000..8e1db2c511 --- /dev/null +++ b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.css @@ -0,0 +1,5 @@ +/* #docregion cross-validation-error-css */ +.cross-validation-error input { + border-left: 5px solid red; +} +/* #enddocregion cross-validation-error-css */ \ No newline at end of file diff --git a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.html b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.html index 36cebbaca4..5edb6500f9 100644 --- a/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.html +++ b/aio/content/examples/form-validation/src/app/reactive/hero-form-reactive.component.html @@ -3,38 +3,67 @@

Reactive Form

+
+
-
+ + +
+ + +
- - - + + + + + + + -
+
-
- Name is required. -
-
- Name must be at least 4 characters long. -
-
- Name cannot be Bob. +
+ Name is required. +
+
+ Name must be at least 4 characters long. +
+
+ Name cannot be Bob. +
+
- -
-
- - +
+ + + + + + +
+ + + +
+ Name cannot match alter ego. +
+ + + + +
+ +
@@ -53,7 +82,9 @@
+ +