X Tutup
Skip to content

Commit 680f7e0

Browse files
committed
fix(core/forms): input[type=text] .valueChanges fires unexpectedly
Closes #4768, #5284 Closes #5401
1 parent 688469c commit 680f7e0

File tree

2 files changed

+56
-36
lines changed

2 files changed

+56
-36
lines changed

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@ const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
2020
// TODO: vsavkin replace the above selector with the one below it once
2121
// https://github.com/angular/angular/issues/3011 is implemented
2222
// selector: '[ng-control],[ng-model],[ng-form-control]',
23-
host: {
24-
'(change)': 'onChange($event.target.value)',
25-
'(input)': 'onChange($event.target.value)',
26-
'(blur)': 'onTouched()'
27-
},
23+
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
2824
bindings: [DEFAULT_VALUE_ACCESSOR]
2925
})
3026
export class DefaultValueAccessor implements ControlValueAccessor {
@@ -40,4 +36,4 @@ export class DefaultValueAccessor implements ControlValueAccessor {
4036

4137
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
4238
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
43-
}
39+
}

modules/angular2/test/common/forms/integration_spec.ts

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,37 @@ export function main() {
7474
var input = fixture.debugElement.query(By.css("input"));
7575

7676
input.nativeElement.value = "updatedValue";
77-
dispatchEvent(input.nativeElement, "change");
77+
dispatchEvent(input.nativeElement, "input");
7878

7979
expect(form.value).toEqual({"login": "updatedValue"});
8080
async.done();
8181
});
8282
}));
8383

84+
it("should ignore the change event for <input type=text>",
85+
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
86+
var form = new ControlGroup({"login": new Control("oldValue")});
87+
88+
var t = `<div [ng-form-model]="form">
89+
<input type="text" ng-control="login">
90+
</div>`;
91+
92+
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
93+
fixture.debugElement.componentInstance.form = form;
94+
fixture.detectChanges();
95+
var input = fixture.debugElement.query(By.css("input"));
96+
97+
input.nativeElement.value = "updatedValue";
98+
99+
ObservableWrapper.subscribe(form.valueChanges,
100+
(value) => { throw 'Should not happen'; });
101+
dispatchEvent(input.nativeElement, "change");
102+
103+
async.done();
104+
});
105+
}));
106+
107+
84108
it("should emit ng-submit event on submit",
85109
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
86110
var t = `<div>
@@ -120,7 +144,7 @@ export function main() {
120144
expect(input.nativeElement.value).toEqual("loginValue");
121145

122146
input.nativeElement.value = "updatedValue";
123-
dispatchEvent(input.nativeElement, "change");
147+
dispatchEvent(input.nativeElement, "input");
124148

125149
expect(control.value).toEqual("updatedValue");
126150
async.done();
@@ -298,7 +322,7 @@ export function main() {
298322
expect(input.nativeElement.value).toEqual("10");
299323

300324
input.nativeElement.value = "20";
301-
dispatchEvent(input.nativeElement, "change");
325+
dispatchEvent(input.nativeElement, "input");
302326

303327
expect(fixture.debugElement.componentInstance.form.value).toEqual({"num": 20});
304328
async.done();
@@ -370,7 +394,7 @@ export function main() {
370394
expect(input.nativeElement.value).toEqual("!aa!");
371395

372396
input.nativeElement.value = "!bb!";
373-
dispatchEvent(input.nativeElement, "change");
397+
dispatchEvent(input.nativeElement, "input");
374398

375399
expect(fixture.debugElement.componentInstance.form.value).toEqual({"name": "bb"});
376400
async.done();
@@ -391,7 +415,7 @@ export function main() {
391415
expect(input.componentInstance.value).toEqual("!aa!");
392416

393417
input.componentInstance.value = "!bb!";
394-
ObservableWrapper.subscribe(input.componentInstance.onChange, (value) => {
418+
ObservableWrapper.subscribe(input.componentInstance.onInput, (value) => {
395419
expect(fixture.debugElement.componentInstance.form.value).toEqual({"name": "bb"});
396420
async.done();
397421
});
@@ -424,9 +448,9 @@ export function main() {
424448
required.nativeElement.value = "";
425449
minLength.nativeElement.value = "1";
426450
maxLength.nativeElement.value = "1234";
427-
dispatchEvent(required.nativeElement, "change");
428-
dispatchEvent(minLength.nativeElement, "change");
429-
dispatchEvent(maxLength.nativeElement, "change");
451+
dispatchEvent(required.nativeElement, "input");
452+
dispatchEvent(minLength.nativeElement, "input");
453+
dispatchEvent(maxLength.nativeElement, "input");
430454

431455
expect(form.hasError("required", ["login"])).toEqual(true);
432456
expect(form.hasError("minlength", ["min"])).toEqual(true);
@@ -437,9 +461,9 @@ export function main() {
437461
required.nativeElement.value = "1";
438462
minLength.nativeElement.value = "123";
439463
maxLength.nativeElement.value = "123";
440-
dispatchEvent(required.nativeElement, "change");
441-
dispatchEvent(minLength.nativeElement, "change");
442-
dispatchEvent(maxLength.nativeElement, "change");
464+
dispatchEvent(required.nativeElement, "input");
465+
dispatchEvent(minLength.nativeElement, "input");
466+
dispatchEvent(maxLength.nativeElement, "input");
443467

444468
expect(form.valid).toEqual(true);
445469

@@ -470,7 +494,7 @@ export function main() {
470494

471495
var input = rootTC.debugElement.query(By.css("input"));
472496
input.nativeElement.value = "expected";
473-
dispatchEvent(input.nativeElement, "change");
497+
dispatchEvent(input.nativeElement, "input");
474498
tick(100);
475499

476500
expect(form.valid).toEqual(true);
@@ -492,7 +516,7 @@ export function main() {
492516
var input = fixture.debugElement.query(By.css("input"));
493517

494518
input.nativeElement.value = "";
495-
dispatchEvent(input.nativeElement, "change");
519+
dispatchEvent(input.nativeElement, "input");
496520

497521
expect(form.valid).toEqual(false);
498522
async.done();
@@ -521,15 +545,15 @@ export function main() {
521545

522546
var input = fixture.debugElement.query(By.css("input"));
523547
input.nativeElement.value = "wrong value";
524-
dispatchEvent(input.nativeElement, "change");
548+
dispatchEvent(input.nativeElement, "input");
525549

526550
expect(form.pending).toEqual(true);
527551
tick();
528552

529553
expect(form.hasError("uniqLogin", ["login"])).toEqual(true);
530554

531555
input.nativeElement.value = "expected";
532-
dispatchEvent(input.nativeElement, "change");
556+
dispatchEvent(input.nativeElement, "input");
533557
tick();
534558

535559
expect(form.valid).toEqual(true);
@@ -575,7 +599,7 @@ export function main() {
575599
var input = fixture.debugElement.query(By.css("input"));
576600

577601
input.nativeElement.value = "updatedValue";
578-
dispatchEvent(input.nativeElement, "change");
602+
dispatchEvent(input.nativeElement, "input");
579603

580604
expect(form.value).toEqual({"nested": {"login": "updatedValue"}});
581605
async.done();
@@ -604,7 +628,7 @@ export function main() {
604628
expect(input.value).toEqual("oldValue");
605629

606630
input.value = "updatedValue";
607-
dispatchEvent(input, "change");
631+
dispatchEvent(input, "input");
608632

609633
tick();
610634
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
@@ -629,7 +653,7 @@ export function main() {
629653
expect(input.value).toEqual("oldValue");
630654

631655
input.value = "updatedValue";
632-
dispatchEvent(input, "change");
656+
dispatchEvent(input, "input");
633657
tick();
634658

635659
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
@@ -763,7 +787,7 @@ export function main() {
763787
expect(input.value).toEqual("oldValue");
764788

765789
input.value = "updatedValue";
766-
dispatchEvent(input, "change");
790+
dispatchEvent(input, "input");
767791
tick();
768792

769793
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
@@ -785,7 +809,7 @@ export function main() {
785809
expect(input.value).toEqual("oldValue");
786810

787811
input.value = "updatedValue";
788-
dispatchEvent(input, "change");
812+
dispatchEvent(input, "input");
789813
tick();
790814

791815
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
@@ -813,7 +837,7 @@ export function main() {
813837
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
814838

815839
input.value = "updatedValue";
816-
dispatchEvent(input, "change");
840+
dispatchEvent(input, "input");
817841
fixture.detectChanges();
818842

819843
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
@@ -840,7 +864,7 @@ export function main() {
840864
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
841865

842866
input.value = "updatedValue";
843-
dispatchEvent(input, "change");
867+
dispatchEvent(input, "input");
844868
fixture.detectChanges();
845869

846870
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
@@ -865,7 +889,7 @@ export function main() {
865889
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
866890

867891
input.value = "updatedValue";
868-
dispatchEvent(input, "change");
892+
dispatchEvent(input, "input");
869893
fixture.detectChanges();
870894

871895
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
@@ -898,7 +922,7 @@ export function main() {
898922
var input = fixture.debugElement.query(By.css("input")).nativeElement;
899923
input.value = "aa";
900924
input.selectionStart = 1;
901-
dispatchEvent(input, "change");
925+
dispatchEvent(input, "input");
902926

903927
tick();
904928
fixture.detectChanges();
@@ -921,7 +945,7 @@ export function main() {
921945
var input = fixture.debugElement.query(By.css("input")).nativeElement;
922946
input.value = "aa";
923947
input.selectionStart = 1;
924-
dispatchEvent(input, "change");
948+
dispatchEvent(input, "input");
925949

926950
tick();
927951
fixture.detectChanges();
@@ -958,7 +982,7 @@ export function main() {
958982

959983
@Directive({
960984
selector: '[wrapped-value]',
961-
host: {'(change)': 'handleOnChange($event.target.value)', '[value]': 'value'}
985+
host: {'(input)': 'handleOnInput($event.target.value)', '[value]': 'value'}
962986
})
963987
class WrappedValue implements ControlValueAccessor {
964988
value;
@@ -971,24 +995,24 @@ class WrappedValue implements ControlValueAccessor {
971995
registerOnChange(fn) { this.onChange = fn; }
972996
registerOnTouched(fn) {}
973997

974-
handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); }
998+
handleOnInput(value) { this.onChange(value.substring(1, value.length - 1)); }
975999
}
9761000

9771001
@Component({selector: "my-input", template: ''})
9781002
class MyInput implements ControlValueAccessor {
979-
@Output('change') onChange: EventEmitter<any> = new EventEmitter();
1003+
@Output('input') onInput: EventEmitter<any> = new EventEmitter();
9801004
value: string;
9811005

9821006
constructor(cd: NgControl) { cd.valueAccessor = this; }
9831007

9841008
writeValue(value) { this.value = `!${value}!`; }
9851009

986-
registerOnChange(fn) { ObservableWrapper.subscribe(this.onChange, fn); }
1010+
registerOnChange(fn) { ObservableWrapper.subscribe(this.onInput, fn); }
9871011

9881012
registerOnTouched(fn) {}
9891013

9901014
dispatchChangeEvent() {
991-
ObservableWrapper.callEmit(this.onChange, this.value.substring(1, this.value.length - 1));
1015+
ObservableWrapper.callEmit(this.onInput, this.value.substring(1, this.value.length - 1));
9921016
}
9931017
}
9941018

0 commit comments

Comments
 (0)
X Tutup