diff --git a/modules/angular2/src/forms/directives/default_value_accessor.ts b/modules/angular2/src/forms/directives/default_value_accessor.ts
index d8b8546672..98c6a9ad18 100644
--- a/modules/angular2/src/forms/directives/default_value_accessor.ts
+++ b/modules/angular2/src/forms/directives/default_value_accessor.ts
@@ -1,6 +1,7 @@
import {Directive} from 'angular2/angular2';
import {ControlDirective} from './control_directive';
import {ControlValueAccessor} from './control_value_accessor';
+import {isBlank} from 'angular2/src/facade/lang';
/**
* 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';
}
})
export class DefaultValueAccessor implements ControlValueAccessor {
- value = null;
+ value: string = null;
onChange: Function;
onTouched: Function;
@@ -44,7 +45,7 @@ export class DefaultValueAccessor implements ControlValueAccessor {
cd.valueAccessor = this;
}
- writeValue(value) { this.value = value; }
+ writeValue(value) { this.value = isBlank(value) ? "" : value; }
registerOnChange(fn): void { this.onChange = fn; }
diff --git a/modules/examples/src/template_driven_forms/index.html b/modules/examples/src/template_driven_forms/index.html
new file mode 100644
index 0000000000..79ca5b3efb
--- /dev/null
+++ b/modules/examples/src/template_driven_forms/index.html
@@ -0,0 +1,19 @@
+
+
+
+ Template Driven Forms
+
+
+
+
+
+ Loading...
+
+
+ $SCRIPTS$
+
+
diff --git a/modules/examples/src/template_driven_forms/index.ts b/modules/examples/src/template_driven_forms/index.ts
new file mode 100644
index 0000000000..61ca94737d
--- /dev/null
+++ b/modules/examples/src/template_driven_forms/index.ts
@@ -0,0 +1,157 @@
+import {bootstrap, onChange, NgIf, Component, Directive, View, Ancestor} from 'angular2/angular2';
+import {
+ formDirectives,
+ ControlDirective,
+ Validators,
+ TemplateDrivenFormDirective
+} from 'angular2/forms';
+
+import {ObservableWrapper} from 'angular2/src/facade/async';
+import {RegExpWrapper, print, isPresent} from 'angular2/src/facade/lang';
+
+import {reflector} from 'angular2/src/reflection/reflection';
+import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
+
+/**
+ * A domain model we are binding the form controls to.
+ */
+class CheckoutModel {
+ firstName: string;
+ middleName: string;
+ lastName: string;
+
+ creditCard: string;
+ amount: number;
+ email: string;
+ comments: string;
+}
+
+/**
+ * Custom validator.
+ */
+@Directive({selector: '[credit-card]'})
+class CreditCardValidator {
+ constructor(c: ControlDirective) {
+ c.validator = Validators.compose([c.validator, CreditCardValidator.validate]);
+ }
+
+ static validate(c): StringMap {
+ if (isPresent(c.value) && RegExpWrapper.test(new RegExp("^\\d{16}$"), c.value)) {
+ return null;
+ } else {
+ return {"invalidCreditCard": true};
+ }
+ }
+}
+
+/**
+ * This is a component that displays an error message.
+ *
+ * For instance,
+ *
+ *
+ *
+ * Will display the "is required" error if the control is empty, and "invalid credit card" if the
+ * control is not empty
+ * but not valid.
+ *
+ * In a real application, this component would receive a service that would map an error code to an
+ * actual error message.
+ * To make it simple, we are using a simple map here.
+ */
+@Component({selector: 'show-error', properties: ['controlPath: control', 'errorTypes: errors']})
+@View({
+ template: `
+ {{errorMessage}}
+ `,
+ directives: [NgIf]
+})
+class ShowError {
+ formDir;
+ controlPath: string;
+ errorTypes: List;
+
+ constructor(@Ancestor() formDir: TemplateDrivenFormDirective) { this.formDir = formDir; }
+
+ get errorMessage() {
+ var c = this.formDir.form.find(this.controlPath);
+ for (var i = 0; i < this.errorTypes.length; ++i) {
+ if (isPresent(c) && c.touched && c.hasError(this.errorTypes[i])) {
+ return this._errorMessage(this.errorTypes[i]);
+ }
+ }
+ return null;
+ }
+
+ _errorMessage(code) {
+ var config = {'required': 'is required', 'invalidCreditCard': 'is invalid credit card number'};
+ return config[code];
+ }
+}
+
+
+@Component({selector: 'template-driven-forms'})
+@View({
+ template: `
+ Checkout Form
+
+
+ `,
+ directives: [formDirectives, CreditCardValidator, ShowError]
+})
+class TemplateDrivenForms {
+ model = new CheckoutModel();
+
+ onSubmit() {
+ print("Submitting:");
+ print(this.model);
+ }
+}
+
+export function main() {
+ reflector.reflectionCapabilities = new ReflectionCapabilities();
+ bootstrap(TemplateDrivenForms);
+}