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

Commit 1212b51

Browse files
committed
docs(cb-form-validation): add template2 - a step between template and reactive
Raises questions about what really separates Forms from ReactiveForms
1 parent f971685 commit 1212b51

15 files changed

+712
-231
lines changed

public/docs/_examples/cb-form-validation/e2e-spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/// <reference path="../_protractor/e2e.d.ts" />
2+
'use strict'; // necessary for node!
23
describeIf(browser.appIsTs || browser.appIsJs, 'Forms Tests', function () {
34

45
beforeEach(function () {

public/docs/_examples/cb-form-validation/ts/app/app.component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { Component } from '@angular/core';
33

44
@Component({
55
selector: 'my-app',
6-
template: `<hero-form-template></hero-form-template>
6+
template: `<hero-form-template1></hero-form-template1>
77
<hr>
8-
<hero-form-reactive></hero-form-reactive>`
8+
<hero-form-template2></hero-form-template2>
9+
<hr>
10+
<hero-form-reactive3></hero-form-reactive3>`
911
})
1012
export class AppComponent { }
Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,47 @@
11
<!-- #docregion -->
22
<div class="container">
33
<div [hidden]="submitted">
4-
<h1>Hero Form (Reactive)</h1>
5-
<form [formGroup]="heroForm" *ngIf="active" (ngSubmit)="onSubmit()">
4+
<h1>Hero Form 3 (Reactive)</h1>
5+
<!-- #docregion form-tag-->
6+
<form [formGroup]="heroForm" *ngIf="active" (ngSubmit)="onSubmit()">
7+
<!-- #enddocregion form-tag-->
68
<div class="form-group">
79
<!-- #docregion name-with-error-msg -->
810
<label for="name">Name</label>
11+
912
<input type="text" id="name" class="form-control"
10-
formControlName="name"
11-
[ngClass]="{'required': isRequired('name')}">
12-
<div *ngIf="formError.name" class="alert alert-danger">
13-
{{ formError.name }}
13+
formControlName="name" required >
14+
15+
<div *ngIf="formErrors.name" class="alert alert-danger">
16+
{{ formErrors.name }}
1417
</div>
1518
<!-- #enddocregion name-with-error-msg -->
1619
</div>
1720

1821
<div class="form-group">
1922
<label for="alterEgo">Alter Ego</label>
2023
<input type="text" id="alterEgo" class="form-control"
21-
formControlName="alterEgo"
22-
[ngClass]="{'required': isRequired('alterEgo')}" >
24+
formControlName="alterEgo" >
2325
</div>
2426

2527
<div class="form-group">
2628
<label for="power">Hero Power</label>
2729
<select id="power" class="form-control"
28-
formControlName="power"
29-
[ngClass]="{'required': isRequired('power')}" >
30+
formControlName="power" required >
3031
<option *ngFor="let p of powers" [value]="p">{{p}}</option>
3132
</select>
32-
<div *ngIf="formError.power" class="alert alert-danger">
33-
{{ formError.power }}
33+
34+
<div *ngIf="formErrors.power" class="alert alert-danger">
35+
{{ formErrors.power }}
3436
</div>
3537
</div>
3638

3739
<button type="submit" class="btn btn-default"
3840
[disabled]="!heroForm.valid">Submit</button>
3941
<button type="button" class="btn btn-default"
40-
(click)="newHero()">New Hero</button>
42+
(click)="addHero()">New Hero</button>
4143
</form>
4244
</div>
4345

44-
<hero-submitted [hero]="model" [(submitted)]="submitted"></hero-submitted>
46+
<hero-submitted [hero]="hero" [(submitted)]="submitted"></hero-submitted>
4547
</div>

public/docs/_examples/cb-form-validation/ts/app/reactive/hero-form-reactive.component.ts

Lines changed: 55 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,104 +4,112 @@
44
import { Component, OnInit } from '@angular/core';
55
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
66

7-
import { Hero } from '../shared/hero';
7+
import { Hero } from '../shared/hero';
8+
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
89

910
@Component({
1011
moduleId: module.id,
11-
selector: 'hero-form-reactive',
12+
selector: 'hero-form-reactive3',
1213
templateUrl: 'hero-form-reactive.component.html'
1314
})
14-
// #docregion class
1515
export class HeroFormReactiveComponent implements OnInit {
1616

1717
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
1818

19-
model = new Hero(18, 'Dr. WhatIsHisWayTooLongName', this.powers[0], 'Dr. What');
19+
hero = new Hero(18, 'Dr. WhatIsHisName', this.powers[0], 'Dr. What');
2020

2121
submitted = false;
2222

23+
// #docregion on-submit
2324
onSubmit() {
2425
this.submitted = true;
25-
this.model = this.heroForm.value;
26+
this.hero = this.heroForm.value;
2627
}
27-
// #enddocregion class
28+
// #enddocregion on-submit
29+
// #enddocregion
2830

2931
// Reset the form with a new hero AND restore 'pristine' class state
3032
// by toggling 'active' flag which causes the form
3133
// to be removed/re-added in a tick via NgIf
3234
// TODO: Workaround until NgForm has a reset method (#6822)
33-
// #docregion new-hero
3435
active = true;
35-
36-
// #docregion class
37-
newHero() {
38-
this.model = new Hero(42, '', '');
36+
// #docregion
37+
// #docregion add-hero
38+
addHero() {
39+
this.hero = new Hero(42, '', '');
3940
this.buildForm();
40-
this.onValueChanged('');
41+
this.onValueChanged();
42+
// #enddocregion add-hero
4143
// #enddocregion class
4244

4345
this.active = false;
4446
setTimeout(() => this.active = true, 0);
45-
// #docregion class
47+
// #docregion
48+
// #docregion add-hero
4649
}
50+
// #enddocregion add-hero
4751

48-
//// New with Reactive Form
49-
52+
// #docregion form-builder
5053
heroForm: FormGroup;
51-
constructor(private builder: FormBuilder) { }
52-
53-
ngOnInit(): void { this.buildForm(); }
54+
constructor(private fb: FormBuilder) { }
5455

55-
formError = {
56-
'name': '',
57-
'power': ''
58-
};
59-
60-
validationMessages = {
61-
'name': {
62-
'required': 'Name is required.',
63-
'minlength': 'Name must be at least 4 characters long.',
64-
'maxlength': 'Name cannot be more than 24 characters long.'
65-
},
66-
'power': {
67-
'required': 'Power is required.'
68-
}
69-
};
56+
ngOnInit(): void {
57+
this.buildForm();
58+
}
7059

7160
buildForm(): void {
72-
this.heroForm = this.builder.group({
73-
'name': [this.model.name, [
61+
this.heroForm = this.fb.group({
62+
// #docregion name-validators
63+
'name': [this.hero.name, [
7464
Validators.required,
7565
Validators.minLength(4),
76-
Validators.maxLength(24)
66+
Validators.maxLength(24),
67+
forbiddenNameValidator(/bob/i)
7768
]
7869
],
79-
'alterEgo': [this.model.alterEgo],
80-
'power': [this.model.power, Validators.required]
70+
// #enddocregion name-validators
71+
'alterEgo': [this.hero.alterEgo],
72+
'power': [this.hero.power, Validators.required]
8173
});
74+
8275
this.heroForm.valueChanges
8376
.subscribe(data => this.onValueChanged(data));
8477
}
8578

86-
onValueChanged(data: any) {
79+
// #enddocregion form-builder
80+
81+
onValueChanged(data?: any) {
8782
const controls = this.heroForm ? this.heroForm.controls : {};
88-
for (const field in this.formError) {
83+
84+
for (const field in this.formErrors) {
8985
// clear previous error message (if any)
90-
this.formError[field] = '';
86+
this.formErrors[field] = '';
9187
const control = controls[field];
88+
9289
if (control && control.dirty && !control.valid) {
9390
const messages = this.validationMessages[field];
9491
for (const key in control.errors) {
95-
this.formError[field] += messages[key] + ' ';
92+
this.formErrors[field] += messages[key] + ' ';
9693
}
9794
}
9895
}
9996
}
10097

101-
isRequired(controlName: string): boolean {
102-
const msgs = this.validationMessages[controlName];
103-
return msgs && msgs['required'];
104-
}
98+
formErrors = {
99+
'name': '',
100+
'power': ''
101+
};
102+
103+
validationMessages = {
104+
'name': {
105+
'required': 'Name is required.',
106+
'minlength': 'Name must be at least 4 characters long.',
107+
'maxlength': 'Name cannot be more than 24 characters long.',
108+
'forbiddenName': 'Someone named "Bob" cannot be a hero.'
109+
},
110+
'power': {
111+
'required': 'Power is required.'
112+
}
113+
};
105114
}
106-
// #enddocregion class
107115
// #enddocregion

public/docs/_examples/cb-form-validation/ts/app/reactive/hero-form-reactive.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { NgModule } from '@angular/core';
33
import { ReactiveFormsModule } from '@angular/forms';
44

5-
import { SharedModule } from '../shared/shared.module';
5+
import { SharedModule } from '../shared/shared.module';
66
import { HeroFormReactiveComponent } from './hero-form-reactive.component';
77

88
@NgModule({
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// #docregion
2+
import { Directive, Input, OnChanges, SimpleChanges } from '@angular/core';
3+
import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } from '@angular/forms';
4+
5+
// #docregion custom-validator
6+
/** A hero's name can't match the given regular expression */
7+
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
8+
return (control: AbstractControl): {[key: string]: any} => {
9+
const name = control.value;
10+
const no = nameRe.test(name);
11+
return no ? {'forbiddenName': {name}} : null;
12+
};
13+
}
14+
// #enddocregion custom-validator
15+
16+
// #docregion directive
17+
@Directive({
18+
selector: '[forbiddenName]',
19+
// #docregion directive-providers
20+
providers: [{provide: NG_VALIDATORS, useExisting: ForbiddenValidatorDirective, multi: true}]
21+
// #enddocregion directive-providers
22+
})
23+
export class ForbiddenValidatorDirective implements Validator, OnChanges {
24+
@Input() forbiddenName: string;
25+
private valFn = Validators.nullValidator;
26+
27+
ngOnChanges(changes: SimpleChanges): void {
28+
const change = changes['forbiddenName'];
29+
if (change) {
30+
const val: string | RegExp = change.currentValue;
31+
const re = val instanceof RegExp ? val : new RegExp(val, 'i');
32+
this.valFn = forbiddenNameValidator(re);
33+
} else {
34+
this.valFn = Validators.nullValidator;
35+
}
36+
}
37+
38+
validate(control: AbstractControl): {[key: string]: any} {
39+
return this.valFn(control);
40+
}
41+
}
42+
// #docregion directive
43+
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// #docregion
2-
import { NgModule } from '@angular/core';
3-
import { CommonModule } from '@angular/common';
2+
import { NgModule } from '@angular/core';
3+
import { CommonModule } from '@angular/common';
44

5-
import { SubmittedComponent } from './submitted.component';
5+
import { ForbiddenValidatorDirective } from './forbidden-name.directive';
6+
import { SubmittedComponent } from './submitted.component';
67

78
@NgModule({
89
imports: [ CommonModule],
9-
declarations: [ SubmittedComponent ],
10-
exports: [ CommonModule, SubmittedComponent ]
10+
declarations: [ ForbiddenValidatorDirective, SubmittedComponent ],
11+
exports: [ ForbiddenValidatorDirective, SubmittedComponent,
12+
CommonModule ]
1113
})
1214
export class SharedModule { }

public/docs/_examples/cb-form-validation/ts/app/template/hero-form-template.module.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
import { NgModule } from '@angular/core';
33
import { FormsModule } from '@angular/forms';
44

5-
import { SharedModule } from '../shared/shared.module';
6-
import { HeroFormTemplateComponent } from './hero-form-template.component';
5+
import { SharedModule } from '../shared/shared.module';
6+
import { HeroFormTemplate1Component } from './hero-form-template1.component';
7+
import { HeroFormTemplate2Component } from './hero-form-template2.component';
78

89
@NgModule({
910
imports: [ SharedModule, FormsModule ],
10-
declarations: [ HeroFormTemplateComponent ],
11-
exports: [ HeroFormTemplateComponent ]
11+
declarations: [ HeroFormTemplate1Component, HeroFormTemplate2Component ],
12+
exports: [ HeroFormTemplate1Component, HeroFormTemplate2Component ]
1213
})
1314
export class HeroFormTemplateModule { }

0 commit comments

Comments
 (0)