Skip to content
This repository was archived by the owner on Dec 4, 2017. It is now read-only.

Commit 3b4a5d5

Browse files
DeborahKwardbell
authored andcommitted
docs(cb-form-validation): create Form Validation Cookbook
1 parent f49b611 commit 3b4a5d5

15 files changed

+641
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/// <reference path="../_protractor/e2e.d.ts" />
2+
describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () {
3+
4+
beforeEach(function () {
5+
browser.get('');
6+
});
7+
8+
it('should display correct title', function () {
9+
expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form');
10+
});
11+
12+
13+
it('should not display message before submit', function () {
14+
let ele = element(by.css('h2'));
15+
expect(ele.isDisplayed()).toBe(false);
16+
});
17+
18+
it('should hide form after submit', function () {
19+
let ele = element.all(by.css('h1')).get(0);
20+
expect(ele.isDisplayed()).toBe(true);
21+
let b = element.all(by.css('button[type=submit]')).get(0);
22+
b.click().then(function() {
23+
expect(ele.isDisplayed()).toBe(false);
24+
});
25+
});
26+
27+
it('should display message after submit', function () {
28+
let b = element.all(by.css('button[type=submit]')).get(0);
29+
b.click().then(function() {
30+
expect(element(by.css('h2')).getText()).toContain('You submitted the following');
31+
});
32+
});
33+
34+
it('should hide form after submit', function () {
35+
let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
36+
expect(alterEgoEle.isDisplayed()).toBe(true);
37+
let submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
38+
submitButtonEle.click().then(function() {
39+
expect(alterEgoEle.isDisplayed()).toBe(false);
40+
});
41+
});
42+
43+
it('should reflect submitted data after submit', function () {
44+
let test = 'testing 1 2 3';
45+
let newValue: string;
46+
let alterEgoEle = element.all(by.css('input[ngcontrol=alterEgo]')).get(0);
47+
alterEgoEle.getAttribute('value').then(function(value) {
48+
// alterEgoEle.sendKeys(test);
49+
sendKeys(alterEgoEle, test);
50+
newValue = value + test;
51+
expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
52+
}).then(function() {
53+
let b = element.all(by.css('button[type=submit]')).get(0);
54+
return b.click();
55+
}).then(function() {
56+
let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
57+
expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
58+
let divEle = element(by.cssContainingText('div', newValue));
59+
expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
60+
});
61+
});
62+
});
63+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// #docplaster
2+
// #docregion
3+
import { Component } from '@angular/core';
4+
5+
@Component({
6+
selector: 'my-app',
7+
template: `<hero-form-template></hero-form-template>
8+
<hr>
9+
<hero-form-model></hero-form-model>`
10+
})
11+
export class AppComponent { }
12+
// #enddocregion
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// #docregion
2+
import { NgModule } from '@angular/core';
3+
import { BrowserModule } from '@angular/platform-browser';
4+
import { FormsModule } from '@angular/forms';
5+
import { ReactiveFormsModule } from '@angular/forms';
6+
7+
import { AppComponent } from './app.component';
8+
import { HeroFormTemplateComponent } from './hero-form-template.component'
9+
import { HeroFormModelComponent } from './hero-form-model.component'
10+
11+
@NgModule({
12+
imports: [
13+
BrowserModule,
14+
FormsModule,
15+
ReactiveFormsModule
16+
],
17+
declarations: [
18+
AppComponent,
19+
HeroFormTemplateComponent,
20+
HeroFormModelComponent
21+
],
22+
bootstrap: [ AppComponent ]
23+
})
24+
export class AppModule { }
25+
// #enddocregion
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!-- #docplaster -->
2+
<!-- #docregion -->
3+
<div class="container">
4+
<div [hidden]="submitted">
5+
<h1>Hero Form (Model-Driven)</h1>
6+
<form [formGroup]="heroForm" *ngIf="active" (ngSubmit)="onSubmit()">
7+
<div class="form-group">
8+
<!-- #docregion name-with-error-msg -->
9+
<label for="name3">Name</label>
10+
<input type="text" id="name3" class="form-control"
11+
formControlName="name"
12+
[ngClass]="{'required': isRequired('name')}">
13+
<div *ngIf="formError.name" class="alert alert-danger">
14+
{{ formError.name }}
15+
</div>
16+
<!-- #enddocregion name-with-error-msg -->
17+
</div>
18+
19+
<div class="form-group">
20+
<label for="alterEgo3">Alter Ego</label>
21+
<input type="text" id="alterEgo3" class="form-control"
22+
formControlName="alterEgo"
23+
[ngClass]="{'required': isRequired('alterEgo')}" >
24+
</div>
25+
26+
<div class="form-group">
27+
<label for="power3">Hero Power</label>
28+
<select id="power3" class="form-control"
29+
formControlName="power"
30+
[ngClass]="{'required': isRequired('power')}" >
31+
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
32+
</select>
33+
<div *ngIf="formError.power" class="alert alert-danger">
34+
{{ formError.power }}
35+
</div>
36+
</div>
37+
38+
<button type="submit" class="btn btn-default" [disabled]="!heroForm.valid">Submit</button>
39+
<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
40+
</form>
41+
</div>
42+
43+
<div [hidden]="!submitted">
44+
<h2>You submitted the following:</h2>
45+
<div class="row">
46+
<div class="col-xs-3">Name</div>
47+
<div class="col-xs-9 pull-left">{{ model.name }}</div>
48+
</div>
49+
<div class="row">
50+
<div class="col-xs-3">Alter Ego</div>
51+
<div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
52+
</div>
53+
<div class="row">
54+
<div class="col-xs-3">Power</div>
55+
<div class="col-xs-9 pull-left">{{ model.power }}</div>
56+
</div>
57+
<br>
58+
<button class="btn btn-default" (click)="submitted=false">Edit</button>
59+
</div>
60+
</div>
61+
<!-- #enddocregion -->
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// #docplaster
2+
// #docregion
3+
import { Component, OnInit } from '@angular/core';
4+
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
5+
6+
import { Hero } from './hero';
7+
8+
@Component({
9+
selector: 'hero-form-model',
10+
templateUrl: 'app/hero-form-model.component.html'
11+
})
12+
// #docregion class
13+
export class HeroFormModelComponent implements OnInit {
14+
heroForm: FormGroup;
15+
formError: { [id: string]: string };
16+
private validationMessages: { [id: string]: { [id: string]: string } };
17+
18+
powers = ['Really Smart', 'Super Flexible',
19+
'Super Hot', 'Weather Changer'];
20+
21+
model = new Hero(18, 'Dr IQ', this.powers[0],
22+
'Chuck Overstreet');
23+
24+
// #enddocregion class
25+
submitted = false;
26+
// #docregion class
27+
constructor(private fb: FormBuilder) {
28+
this.formError = {
29+
'name': '',
30+
'alterEgo': '',
31+
'power': ''
32+
};
33+
this.validationMessages = {
34+
'name': {
35+
'required': 'Name is required.',
36+
'minlength': 'Name must be at least 4 characters long.',
37+
'maxlength': 'Name cannot be more than 24 characters long.'
38+
},
39+
'power': {
40+
'required': 'Power is required.'
41+
}
42+
};
43+
}
44+
45+
ngOnInit(): void {
46+
this.buildForm();
47+
}
48+
49+
buildForm(): void {
50+
this.heroForm = this.fb.group({
51+
'name': [this.model.name,
52+
[Validators.required,
53+
Validators.minLength(4),
54+
Validators.maxLength(24)]],
55+
'alterEgo': [this.model.alterEgo],
56+
'power': [this.model.power, Validators.required]
57+
});
58+
59+
this.heroForm.valueChanges
60+
.subscribe(data => this.onValueChanged(data));
61+
}
62+
63+
onValueChanged(data: any) {
64+
for (let field in this.formError) {
65+
if (this.formError.hasOwnProperty(field)) {
66+
let hasError = (this.heroForm.controls[field].dirty) &&
67+
!this.heroForm.controls[field].valid;
68+
this.formError[field] = '';
69+
if (hasError) {
70+
for (let key in this.heroForm.controls[field].errors) {
71+
if (this.heroForm.controls[field].errors.hasOwnProperty(key)) {
72+
this.formError[field] += this.validationMessages[field][key] + ' ';
73+
}
74+
}
75+
}
76+
}
77+
}
78+
}
79+
// #enddocregion class
80+
onSubmit() {
81+
this.submitted = true;
82+
this.model = this.heroForm.value;
83+
}
84+
85+
isRequired(controlName: string): boolean {
86+
if (Object.keys(this.validationMessages).includes(controlName)) {
87+
return Object.keys(this.validationMessages[controlName]).includes('required');}
88+
return false;
89+
}
90+
// Reset the form with a new hero AND restore 'pristine' class state
91+
// by toggling 'active' flag which causes the form
92+
// to be removed/re-added in a tick via NgIf
93+
// TODO: Workaround until NgForm has a reset method (#6822)
94+
// #docregion new-hero
95+
active = true;
96+
97+
newHero() {
98+
this.model = new Hero(42, '', '');
99+
this.buildForm();
100+
this.onValueChanged('');
101+
this.active = false;
102+
setTimeout(() => this.active = true, 0);
103+
}
104+
// #docregion class
105+
}
106+
// #enddocregion class
107+
// #enddocregion
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<!-- #docplaster -->
2+
<!-- #docregion -->
3+
<div class="container">
4+
<div [hidden]="submitted">
5+
<h1>Hero Form (Template-Driven)</h1>
6+
<form *ngIf="active"
7+
(ngSubmit)="onSubmit()"
8+
#heroForm="ngForm">
9+
<div class="form-group">
10+
<!-- #docregion name-with-error-msg -->
11+
<label for="name">Name</label>
12+
<input type="text" id="name" class="form-control"
13+
required minlength="4" maxlength="24"
14+
name="name" [(ngModel)]="model.name"
15+
#name="ngModel" >
16+
<div *ngIf="name.errors && (name.dirty || name.touched)"
17+
class="alert alert-danger">
18+
<div [hidden]="!name.errors.required">
19+
Name is required
20+
</div>
21+
<div [hidden]="!name.errors.minlength">
22+
Name must be at least 4 characters long.
23+
</div>
24+
<div [hidden]="!name.errors.maxlength">
25+
Name cannot be more than 24 characters long.
26+
</div>
27+
</div>
28+
<!-- #enddocregion name-with-error-msg -->
29+
</div>
30+
31+
<div class="form-group">
32+
<label for="alterEgo">Alter Ego</label>
33+
<input type="text" id="alterEgo" class="form-control"
34+
name="alterEgo" [(ngModel)]="model.alterEgo">
35+
</div>
36+
37+
<div class="form-group">
38+
<label for="power">Hero Power</label>
39+
<select id="power" class="form-control"
40+
required
41+
name="power" [(ngModel)]="model.power"
42+
#power="ngModel" >
43+
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
44+
</select>
45+
<div *ngIf="power.errors && power.touched" class="alert alert-danger">
46+
<div [hidden]="!power.errors.required">Power is required</div>
47+
</div>
48+
</div>
49+
50+
<button type="submit" class="btn btn-default" [disabled]="!heroForm.form.valid">Submit</button>
51+
<button type="button" class="btn btn-default" (click)="newHero()">New Hero</button>
52+
</form>
53+
</div>
54+
55+
<div [hidden]="!submitted">
56+
<h2>You submitted the following:</h2>
57+
<div class="row">
58+
<div class="col-xs-3">Name</div>
59+
<div class="col-xs-9 pull-left">{{ model.name }}</div>
60+
</div>
61+
<div class="row">
62+
<div class="col-xs-3">Alter Ego</div>
63+
<div class="col-xs-9 pull-left">{{ model.alterEgo }}</div>
64+
</div>
65+
<div class="row">
66+
<div class="col-xs-3">Power</div>
67+
<div class="col-xs-9 pull-left">{{ model.power }}</div>
68+
</div>
69+
<br>
70+
<button class="btn btn-default" (click)="submitted=false">Edit</button>
71+
</div>
72+
</div>
73+
<!-- #enddocregion -->
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// #docplaster
2+
// #docregion
3+
import { Component } from '@angular/core';
4+
5+
import { Hero } from './hero';
6+
7+
@Component({
8+
selector: 'hero-form-template',
9+
templateUrl: 'app/hero-form-template.component.html'
10+
})
11+
// #docregion class
12+
export class HeroFormTemplateComponent {
13+
14+
powers = ['Really Smart', 'Super Flexible',
15+
'Super Hot', 'Weather Changer'];
16+
17+
model = new Hero(18, 'Dr IQ', this.powers[0],
18+
'Chuck Overstreet');
19+
// #enddocregion class
20+
submitted = false;
21+
22+
onSubmit() { this.submitted = true; }
23+
24+
// Reset the form with a new hero AND restore 'pristine' class state
25+
// by toggling 'active' flag which causes the form
26+
// to be removed/re-added in a tick via NgIf
27+
// TODO: Workaround until NgForm has a reset method (#6822)
28+
active = true;
29+
30+
newHero() {
31+
this.model = new Hero(42, '', '');
32+
this.active = false;
33+
setTimeout(()=> this.active=true, 0);
34+
}
35+
// #docregion class
36+
}
37+
// #enddocregion class
38+
// #enddocregion

0 commit comments

Comments
 (0)