Skip to content

Commit

Permalink
fix(default_value_accessor): support custom input elements that fire …
Browse files Browse the repository at this point in the history
…custom change events.

Closes #4878
  • Loading branch information
tbosch committed Oct 26, 2015
1 parent de6774c commit 56a9b02
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)',
Expand Down
46 changes: 44 additions & 2 deletions modules/angular2/test/core/forms/integration_spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, Directive, View} from 'angular2/angular2';
import {Component, Directive, View, Output, EventEmitter} from 'angular2/angular2';
import {
RootTestComponent,
afterEach,
Expand Down Expand Up @@ -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", () => {
Expand Down Expand Up @@ -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 = `<div [ng-form-model]="form">
<my-input ng-control="name"></my-input>
</div>`;

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", () => {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 56a9b02

Please sign in to comment.