fix(core/forms): input[type=text] .valueChanges fires unexpectedly
Closes #4768, #5284 Closes #5401
This commit is contained in:
parent
688469c221
commit
680f7e0299
|
@ -20,11 +20,7 @@ const DEFAULT_VALUE_ACCESSOR = CONST_EXPR(new Provider(
|
|||
// 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)',
|
||||
'(blur)': 'onTouched()'
|
||||
},
|
||||
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
bindings: [DEFAULT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
|
@ -40,4 +36,4 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
|||
|
||||
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => void): void { this.onTouched = fn; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,13 +74,37 @@ export function main() {
|
|||
var input = fixture.debugElement.query(By.css("input"));
|
||||
|
||||
input.nativeElement.value = "updatedValue";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(form.value).toEqual({"login": "updatedValue"});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("should ignore the change event for <input type=text>",
|
||||
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
|
||||
var form = new ControlGroup({"login": new Control("oldValue")});
|
||||
|
||||
var t = `<div [ng-form-model]="form">
|
||||
<input type="text" ng-control="login">
|
||||
</div>`;
|
||||
|
||||
tcb.overrideTemplate(MyComp, t).createAsync(MyComp).then((fixture) => {
|
||||
fixture.debugElement.componentInstance.form = form;
|
||||
fixture.detectChanges();
|
||||
var input = fixture.debugElement.query(By.css("input"));
|
||||
|
||||
input.nativeElement.value = "updatedValue";
|
||||
|
||||
ObservableWrapper.subscribe(form.valueChanges,
|
||||
(value) => { throw 'Should not happen'; });
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it("should emit ng-submit event on submit",
|
||||
inject([TestComponentBuilder], fakeAsync((tcb: TestComponentBuilder) => {
|
||||
var t = `<div>
|
||||
|
@ -120,7 +144,7 @@ export function main() {
|
|||
expect(input.nativeElement.value).toEqual("loginValue");
|
||||
|
||||
input.nativeElement.value = "updatedValue";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(control.value).toEqual("updatedValue");
|
||||
async.done();
|
||||
|
@ -298,7 +322,7 @@ export function main() {
|
|||
expect(input.nativeElement.value).toEqual("10");
|
||||
|
||||
input.nativeElement.value = "20";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({"num": 20});
|
||||
async.done();
|
||||
|
@ -370,7 +394,7 @@ export function main() {
|
|||
expect(input.nativeElement.value).toEqual("!aa!");
|
||||
|
||||
input.nativeElement.value = "!bb!";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({"name": "bb"});
|
||||
async.done();
|
||||
|
@ -391,7 +415,7 @@ export function main() {
|
|||
expect(input.componentInstance.value).toEqual("!aa!");
|
||||
|
||||
input.componentInstance.value = "!bb!";
|
||||
ObservableWrapper.subscribe(input.componentInstance.onChange, (value) => {
|
||||
ObservableWrapper.subscribe(input.componentInstance.onInput, (value) => {
|
||||
expect(fixture.debugElement.componentInstance.form.value).toEqual({"name": "bb"});
|
||||
async.done();
|
||||
});
|
||||
|
@ -424,9 +448,9 @@ export function main() {
|
|||
required.nativeElement.value = "";
|
||||
minLength.nativeElement.value = "1";
|
||||
maxLength.nativeElement.value = "1234";
|
||||
dispatchEvent(required.nativeElement, "change");
|
||||
dispatchEvent(minLength.nativeElement, "change");
|
||||
dispatchEvent(maxLength.nativeElement, "change");
|
||||
dispatchEvent(required.nativeElement, "input");
|
||||
dispatchEvent(minLength.nativeElement, "input");
|
||||
dispatchEvent(maxLength.nativeElement, "input");
|
||||
|
||||
expect(form.hasError("required", ["login"])).toEqual(true);
|
||||
expect(form.hasError("minlength", ["min"])).toEqual(true);
|
||||
|
@ -437,9 +461,9 @@ export function main() {
|
|||
required.nativeElement.value = "1";
|
||||
minLength.nativeElement.value = "123";
|
||||
maxLength.nativeElement.value = "123";
|
||||
dispatchEvent(required.nativeElement, "change");
|
||||
dispatchEvent(minLength.nativeElement, "change");
|
||||
dispatchEvent(maxLength.nativeElement, "change");
|
||||
dispatchEvent(required.nativeElement, "input");
|
||||
dispatchEvent(minLength.nativeElement, "input");
|
||||
dispatchEvent(maxLength.nativeElement, "input");
|
||||
|
||||
expect(form.valid).toEqual(true);
|
||||
|
||||
|
@ -470,7 +494,7 @@ export function main() {
|
|||
|
||||
var input = rootTC.debugElement.query(By.css("input"));
|
||||
input.nativeElement.value = "expected";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
tick(100);
|
||||
|
||||
expect(form.valid).toEqual(true);
|
||||
|
@ -492,7 +516,7 @@ export function main() {
|
|||
var input = fixture.debugElement.query(By.css("input"));
|
||||
|
||||
input.nativeElement.value = "";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(form.valid).toEqual(false);
|
||||
async.done();
|
||||
|
@ -521,7 +545,7 @@ export function main() {
|
|||
|
||||
var input = fixture.debugElement.query(By.css("input"));
|
||||
input.nativeElement.value = "wrong value";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(form.pending).toEqual(true);
|
||||
tick();
|
||||
|
@ -529,7 +553,7 @@ export function main() {
|
|||
expect(form.hasError("uniqLogin", ["login"])).toEqual(true);
|
||||
|
||||
input.nativeElement.value = "expected";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
tick();
|
||||
|
||||
expect(form.valid).toEqual(true);
|
||||
|
@ -575,7 +599,7 @@ export function main() {
|
|||
var input = fixture.debugElement.query(By.css("input"));
|
||||
|
||||
input.nativeElement.value = "updatedValue";
|
||||
dispatchEvent(input.nativeElement, "change");
|
||||
dispatchEvent(input.nativeElement, "input");
|
||||
|
||||
expect(form.value).toEqual({"nested": {"login": "updatedValue"}});
|
||||
async.done();
|
||||
|
@ -604,7 +628,7 @@ export function main() {
|
|||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
|
||||
tick();
|
||||
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
|
||||
|
@ -629,7 +653,7 @@ export function main() {
|
|||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
tick();
|
||||
|
||||
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
|
||||
|
@ -763,7 +787,7 @@ export function main() {
|
|||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
tick();
|
||||
|
||||
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
|
||||
|
@ -785,7 +809,7 @@ export function main() {
|
|||
expect(input.value).toEqual("oldValue");
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
tick();
|
||||
|
||||
expect(fixture.debugElement.componentInstance.name).toEqual("updatedValue");
|
||||
|
@ -813,7 +837,7 @@ export function main() {
|
|||
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
|
||||
|
@ -840,7 +864,7 @@ export function main() {
|
|||
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
|
||||
|
@ -865,7 +889,7 @@ export function main() {
|
|||
expect(sortedClassList(input)).toEqual(["ng-invalid", "ng-pristine", "ng-touched"]);
|
||||
|
||||
input.value = "updatedValue";
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(sortedClassList(input)).toEqual(["ng-dirty", "ng-touched", "ng-valid"]);
|
||||
|
@ -898,7 +922,7 @@ export function main() {
|
|||
var input = fixture.debugElement.query(By.css("input")).nativeElement;
|
||||
input.value = "aa";
|
||||
input.selectionStart = 1;
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
@ -921,7 +945,7 @@ export function main() {
|
|||
var input = fixture.debugElement.query(By.css("input")).nativeElement;
|
||||
input.value = "aa";
|
||||
input.selectionStart = 1;
|
||||
dispatchEvent(input, "change");
|
||||
dispatchEvent(input, "input");
|
||||
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
@ -958,7 +982,7 @@ export function main() {
|
|||
|
||||
@Directive({
|
||||
selector: '[wrapped-value]',
|
||||
host: {'(change)': 'handleOnChange($event.target.value)', '[value]': 'value'}
|
||||
host: {'(input)': 'handleOnInput($event.target.value)', '[value]': 'value'}
|
||||
})
|
||||
class WrappedValue implements ControlValueAccessor {
|
||||
value;
|
||||
|
@ -971,24 +995,24 @@ class WrappedValue implements ControlValueAccessor {
|
|||
registerOnChange(fn) { this.onChange = fn; }
|
||||
registerOnTouched(fn) {}
|
||||
|
||||
handleOnChange(value) { this.onChange(value.substring(1, value.length - 1)); }
|
||||
handleOnInput(value) { this.onChange(value.substring(1, value.length - 1)); }
|
||||
}
|
||||
|
||||
@Component({selector: "my-input", template: ''})
|
||||
class MyInput implements ControlValueAccessor {
|
||||
@Output('change') onChange: EventEmitter<any> = new EventEmitter();
|
||||
@Output('input') onInput: EventEmitter<any> = new EventEmitter();
|
||||
value: string;
|
||||
|
||||
constructor(cd: NgControl) { cd.valueAccessor = this; }
|
||||
|
||||
writeValue(value) { this.value = `!${value}!`; }
|
||||
|
||||
registerOnChange(fn) { ObservableWrapper.subscribe(this.onChange, fn); }
|
||||
registerOnChange(fn) { ObservableWrapper.subscribe(this.onInput, fn); }
|
||||
|
||||
registerOnTouched(fn) {}
|
||||
|
||||
dispatchChangeEvent() {
|
||||
ObservableWrapper.callEmit(this.onChange, this.value.substring(1, this.value.length - 1));
|
||||
ObservableWrapper.callEmit(this.onInput, this.value.substring(1, this.value.length - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue