diff --git a/modules/angular2/src/forms/directives/form_model_directive.ts b/modules/angular2/src/forms/directives/form_model_directive.ts index c604a5eb8c..838b65c202 100644 --- a/modules/angular2/src/forms/directives/form_model_directive.ts +++ b/modules/angular2/src/forms/directives/form_model_directive.ts @@ -1,5 +1,7 @@ import {CONST_EXPR} from 'angular2/src/facade/lang'; import {List, ListWrapper} from 'angular2/src/facade/collection'; +import {ObservableWrapper, EventEmitter} from 'angular2/src/facade/async'; + import {Directive, onChange} from 'angular2/angular2'; import {FORWARD_REF, Binding} from 'angular2/di'; import {ControlDirective} from './control_directive'; @@ -57,11 +59,16 @@ const formDirectiveBinding = CONST_EXPR( selector: '[ng-form-model]', hostInjector: [formDirectiveBinding], properties: ['form: ng-form-model'], - lifecycle: [onChange] + lifecycle: [onChange], + hostListeners: { + 'submit': 'onSubmit()', + }, + events: ['ngSubmit'] }) export class FormModelDirective extends ControlContainerDirective implements FormDirective { form: ControlGroup = null; directives: List; + ngSubmit = new EventEmitter(); constructor() { super(); @@ -94,6 +101,11 @@ export class FormModelDirective extends ControlContainerDirective implements For c.updateValue(value); } + onSubmit() { + ObservableWrapper.callNext(this.ngSubmit, null); + return false; + } + _updateDomValue() { ListWrapper.forEach(this.directives, dir => { var c: any = this.form.find(dir.path); diff --git a/modules/angular2/src/forms/directives/template_driven_form_directive.ts b/modules/angular2/src/forms/directives/template_driven_form_directive.ts index 7ca98c8028..11c7ea01b4 100644 --- a/modules/angular2/src/forms/directives/template_driven_form_directive.ts +++ b/modules/angular2/src/forms/directives/template_driven_form_directive.ts @@ -1,4 +1,4 @@ -import {PromiseWrapper} from 'angular2/src/facade/async'; +import {PromiseWrapper, ObservableWrapper, EventEmitter} from 'angular2/src/facade/async'; import {StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection'; import {isPresent, CONST_EXPR} from 'angular2/src/facade/lang'; import {Directive} from 'angular2/src/core/annotations/decorators'; @@ -15,11 +15,16 @@ const formDirectiveBinding = CONST_EXPR(new Binding( @Directive({ selector: 'form:not([ng-no-form]):not([ng-form-model]),ng-form,[ng-form]', - hostInjector: [formDirectiveBinding] + hostInjector: [formDirectiveBinding], + hostListeners: { + 'submit': 'onSubmit()', + }, + events: ['ngSubmit'] }) export class TemplateDrivenFormDirective extends ControlContainerDirective implements FormDirective { form: ControlGroup; + ngSubmit = new EventEmitter(); constructor() { super(); @@ -34,12 +39,15 @@ export class TemplateDrivenFormDirective extends ControlContainerDirective imple get value(): any { return this.form.value; } + get errors(): any { return this.form.errors; } + addControl(dir: ControlDirective): void { this._later(_ => { - var group = this._findContainer(dir.path); + var container = this._findContainer(dir.path); var c = new Control(""); setUpControl(c, dir); - group.addControl(dir.name, c); + container.addControl(dir.name, c); + c.updateValidity(); }); } @@ -47,23 +55,28 @@ export class TemplateDrivenFormDirective extends ControlContainerDirective imple removeControl(dir: ControlDirective): void { this._later(_ => { - var c = this._findContainer(dir.path); - if (isPresent(c)) c.removeControl(dir.name) + var container = this._findContainer(dir.path); + if (isPresent(container)) { + container.removeControl(dir.name) container.updateValidity(); + } }); } addControlGroup(dir: ControlGroupDirective): void { this._later(_ => { - var group = this._findContainer(dir.path); + var container = this._findContainer(dir.path); var c = new ControlGroup({}); - group.addControl(dir.name, c); + container.addControl(dir.name, c); + c.updateValidity(); }); } removeControlGroup(dir: ControlGroupDirective): void { this._later(_ => { - var c = this._findContainer(dir.path); - if (isPresent(c)) c.removeControl(dir.name) + var container = this._findContainer(dir.path); + if (isPresent(container)) { + container.removeControl(dir.name) container.updateValidity(); + } }); } @@ -74,6 +87,11 @@ export class TemplateDrivenFormDirective extends ControlContainerDirective imple }); } + onSubmit() { + ObservableWrapper.callNext(this.ngSubmit, null); + return false; + } + _findContainer(path: List): ControlGroup { ListWrapper.removeLast(path); return this.form.find(path); diff --git a/modules/angular2/test/forms/integration_spec.ts b/modules/angular2/test/forms/integration_spec.ts index 456f480f42..8e85898bbb 100644 --- a/modules/angular2/test/forms/integration_spec.ts +++ b/modules/angular2/test/forms/integration_spec.ts @@ -74,6 +74,25 @@ export function main() { }); })); + it("should emit ng-submit event on submit", + inject([TestBed], fakeAsync(tb => { + var form = new ControlGroup({}); + var ctx = MyComp.create({form: form, name: 'old'}); + + var t = + `
`; + + tb.createView(MyComp, {context: ctx, html: t}) + .then((view) => { + var form = view.querySelector("form"); + + dispatchEvent(form, "submit"); + tick(); + + expect(ctx.name).toEqual("updated"); + }); + }))); + it("should work with single controls", inject([TestBed, AsyncTestCompleter], (tb, async) => { var control = new Control("loginValue"); var ctx = MyComp.create({form: control}); @@ -473,6 +492,23 @@ export function main() { }); }))); + it("should emit ng-submit event on submit", + inject([TestBed], fakeAsync(tb => { + var ctx = MyComp.create({name: 'old'}); + + var t = `
`; + + tb.createView(MyComp, {context: ctx, html: t}) + .then((view) => { + var form = view.querySelector("form"); + + dispatchEvent(form, "submit"); + tick(); + + expect(ctx.name).toEqual("updated"); + }); + }))); + it("should not create a template-driven form when ng-no-form is used", inject([TestBed], fakeAsync(tb => { var ctx = MyComp.create({name: null});