diff --git a/modules/angular2/src/core/forms/directives/default_value_accessor.ts b/modules/angular2/src/core/forms/directives/default_value_accessor.ts index 7ba3cb4b2e..9c1d017a29 100644 --- a/modules/angular2/src/core/forms/directives/default_value_accessor.ts +++ b/modules/angular2/src/core/forms/directives/default_value_accessor.ts @@ -19,7 +19,11 @@ const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider( * ``` */ @Directive({ - selector: '[ng-control],[ng-model],[ng-form-control]', + selector: + 'input:not([type=checkbox])[ng-control],textarea[ng-control],input:not([type=checkbox])[ng-form-control],textarea[ng-form-control],input:not([type=checkbox])[ng-model],textarea[ng-model]', + // TODO: vsavkin replace the above selector with the one below it once + // https://github.com/angular/angular/issues/3011 is implemented + // selector: '[ng-control],[ng-model],[ng-form-control]', host: { '(change)': 'onChange($event.target.value)', '(input)': 'onChange($event.target.value)', diff --git a/modules/angular2/test/core/forms/integration_spec.ts b/modules/angular2/test/core/forms/integration_spec.ts index 61bcfba953..9b4e4f6d8e 100644 --- a/modules/angular2/test/core/forms/integration_spec.ts +++ b/modules/angular2/test/core/forms/integration_spec.ts @@ -1,4 +1,4 @@ -import {Component, Directive, View} from 'angular2/angular2'; +import {Component, Directive, View, Output, EventEmitter} from 'angular2/angular2'; import { RootTestComponent, afterEach, @@ -32,6 +32,7 @@ import { } from 'angular2/core'; import {By} from 'angular2/src/core/debug'; import {ListWrapper} from 'angular2/src/core/facade/collection'; +import {ObservableWrapper} from 'angular2/src/core/facade/async'; export function main() { describe("integration tests", () => { @@ -366,6 +367,29 @@ export function main() { async.done(); }); })); + + it("should support custom value accessors on non builtin input elements that fire a change event without a 'target' property", + inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => { + var t = `
+ +
`; + + tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((rootTC) => { + rootTC.debugElement.componentInstance.form = + new ControlGroup({"name": new Control("aa")}); + rootTC.detectChanges(); + var input = rootTC.debugElement.query(By.css("my-input")); + expect(input.componentInstance.value).toEqual("!aa!"); + + input.componentInstance.value = "!bb!"; + ObservableWrapper.subscribe(input.componentInstance.onChange, (value) => { + expect(rootTC.debugElement.componentInstance.form.value).toEqual({"name": "bb"}); + async.done(); + }); + input.componentInstance.dispatchChangeEvent(); + }); + })); + }); describe("validations", () => { @@ -872,8 +896,26 @@ class WrappedValue implements ControlValueAccessor { handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); } } +@Component({selector: "my-input", template: ''}) +class MyInput implements ControlValueAccessor { + @Output('change') onChange: EventEmitter = new EventEmitter(); + value: string; + + constructor(cd: NgControl) { cd.valueAccessor = this; } + + writeValue(value) { this.value = `!${value}!`; } + + registerOnChange(fn) { ObservableWrapper.subscribe(this.onChange, fn); } + + registerOnTouched(fn) {} + + dispatchChangeEvent() { + ObservableWrapper.callNext(this.onChange, this.value.substring(1, this.value.length - 1)); + } +} + @Component({selector: "my-comp"}) -@View({directives: [FORM_DIRECTIVES, WrappedValue, NgIf, NgFor]}) +@View({directives: [FORM_DIRECTIVES, WrappedValue, MyInput, NgIf, NgFor]}) class MyComp { form: any; name: string;