Skip to content

Commit 3eff7be

Browse files
committed
examples(forms): added an example of using template-driven forms
1 parent e7e82cb commit 3eff7be

File tree

3 files changed

+179
-2
lines changed

3 files changed

+179
-2
lines changed

modules/angular2/src/forms/directives/default_value_accessor.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Directive} from 'angular2/angular2';
22
import {ControlDirective} from './control_directive';
33
import {ControlValueAccessor} from './control_value_accessor';
4+
import {isBlank} from 'angular2/src/facade/lang';
45

56
/**
67
* The default accessor for writing a value and listening to changes that is used by a
@@ -34,7 +35,7 @@ import {ControlValueAccessor} from './control_value_accessor';
3435
}
3536
})
3637
export class DefaultValueAccessor implements ControlValueAccessor {
37-
value = null;
38+
value: string = null;
3839
onChange: Function;
3940
onTouched: Function;
4041

@@ -44,7 +45,7 @@ export class DefaultValueAccessor implements ControlValueAccessor {
4445
cd.valueAccessor = this;
4546
}
4647

47-
writeValue(value) { this.value = value; }
48+
writeValue(value) { this.value = isBlank(value) ? "" : value; }
4849

4950
registerOnChange(fn): void { this.onChange = fn; }
5051

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<title>Template Driven Forms</title>
5+
<style>
6+
.ng-touched.ng-invalid {
7+
border-color: red;
8+
}
9+
</style>
10+
</head>
11+
12+
<body>
13+
<template-driven-forms>
14+
Loading...
15+
</template-driven-forms>
16+
17+
$SCRIPTS$
18+
</body>
19+
</html>
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import {bootstrap, onChange, NgIf, Component, Directive, View, Ancestor} from 'angular2/angular2';
2+
import {
3+
formDirectives,
4+
ControlDirective,
5+
Validators,
6+
TemplateDrivenFormDirective
7+
} from 'angular2/forms';
8+
9+
import {ObservableWrapper} from 'angular2/src/facade/async';
10+
import {RegExpWrapper, print, isPresent} from 'angular2/src/facade/lang';
11+
12+
import {reflector} from 'angular2/src/reflection/reflection';
13+
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
14+
15+
/**
16+
* A domain model we are binding the form controls to.
17+
*/
18+
class CheckoutModel {
19+
firstName: string;
20+
middleName: string;
21+
lastName: string;
22+
23+
creditCard: string;
24+
amount: number;
25+
email: string;
26+
comments: string;
27+
}
28+
29+
/**
30+
* Custom validator.
31+
*/
32+
@Directive({selector: '[credit-card]'})
33+
class CreditCardValidator {
34+
constructor(c: ControlDirective) {
35+
c.validator = Validators.compose([c.validator, CreditCardValidator.validate]);
36+
}
37+
38+
static validate(c): StringMap<string, boolean> {
39+
if (isPresent(c.value) && RegExpWrapper.test(new RegExp("^\\d{16}$"), c.value)) {
40+
return null;
41+
} else {
42+
return {"invalidCreditCard": true};
43+
}
44+
}
45+
}
46+
47+
/**
48+
* This is a component that displays an error message.
49+
*
50+
* For instance,
51+
*
52+
* <show-error control="creditCard" [errors]="['required', 'invalidCreditCard']"></show-error>
53+
*
54+
* Will display the "is required" error if the control is empty, and "invalid credit card" if the
55+
* control is not empty
56+
* but not valid.
57+
*
58+
* In a real application, this component would receive a service that would map an error code to an
59+
* actual error message.
60+
* To make it simple, we are using a simple map here.
61+
*/
62+
@Component({selector: 'show-error', properties: ['controlPath: control', 'errorTypes: errors']})
63+
@View({
64+
template: `
65+
<span *ng-if="errorMessage !== null">{{errorMessage}}</span>
66+
`,
67+
directives: [NgIf]
68+
})
69+
class ShowError {
70+
formDir;
71+
controlPath: string;
72+
errorTypes: List<string>;
73+
74+
constructor(@Ancestor() formDir: TemplateDrivenFormDirective) { this.formDir = formDir; }
75+
76+
get errorMessage() {
77+
var c = this.formDir.form.find(this.controlPath);
78+
for (var i = 0; i < this.errorTypes.length; ++i) {
79+
if (isPresent(c) && c.touched && c.hasError(this.errorTypes[i])) {
80+
return this._errorMessage(this.errorTypes[i]);
81+
}
82+
}
83+
return null;
84+
}
85+
86+
_errorMessage(code) {
87+
var config = {'required': 'is required', 'invalidCreditCard': 'is invalid credit card number'};
88+
return config[code];
89+
}
90+
}
91+
92+
93+
@Component({selector: 'template-driven-forms'})
94+
@View({
95+
template: `
96+
<h1>Checkout Form</h1>
97+
98+
<form (ng-submit)="onSubmit()" #f="form">
99+
<p>
100+
<label for="firstName">First Name</label>
101+
<input type="text" id="firstName" ng-control="firstName" [(ng-model)]="model.firstName" required>
102+
<show-error control="firstName" [errors]="['required']"></show-error>
103+
</p>
104+
105+
<p>
106+
<label for="middleName">Middle Name</label>
107+
<input type="text" id="middleName" ng-control="middleName" [(ng-model)]="model.middleName">
108+
</p>
109+
110+
<p>
111+
<label for="lastName">Last Name</label>
112+
<input type="text" id="lastName" ng-control="lastName" [(ng-model)]="model.lastName" required>
113+
<show-error control="lastName" [errors]="['required']"></show-error>
114+
</p>
115+
116+
<p>
117+
<label for="creditCard">Credit Card</label>
118+
<input type="text" id="creditCard" ng-control="creditCard" [(ng-model)]="model.creditCard" required credit-card>
119+
<show-error control="creditCard" [errors]="['required', 'invalidCreditCard']"></show-error>
120+
</p>
121+
122+
<p>
123+
<label for="amount">Amount</label>
124+
<input type="number" id="amount" ng-control="amount" [(ng-model)]="model.amount" required>
125+
<show-error control="amount" [errors]="['required']"></show-error>
126+
</p>
127+
128+
<p>
129+
<label for="email">Email</label>
130+
<input type="email" id="email" ng-control="email" [(ng-model)]="model.email" required>
131+
<show-error control="email" [errors]="['required']"></show-error>
132+
</p>
133+
134+
<p>
135+
<label for="comments">Comments</label>
136+
<textarea id="comments" ng-control="comments" [(ng-model)]="model.comments">
137+
</textarea>
138+
</p>
139+
140+
<button type="submit" [disabled]="!f.form.valid">Submit</button>
141+
</form>
142+
`,
143+
directives: [formDirectives, CreditCardValidator, ShowError]
144+
})
145+
class TemplateDrivenForms {
146+
model = new CheckoutModel();
147+
148+
onSubmit() {
149+
print("Submitting:");
150+
print(this.model);
151+
}
152+
}
153+
154+
export function main() {
155+
reflector.reflectionCapabilities = new ReflectionCapabilities();
156+
bootstrap(TemplateDrivenForms);
157+
}

0 commit comments

Comments
 (0)