fix(forms): update dirty before emitting value change (#10362)
Closes #5328
This commit is contained in:
parent
a32c4ad2f0
commit
7c76a75452
|
@ -45,8 +45,8 @@ export function setUpControl(control: FormControl, dir: NgControl): void {
|
||||||
// view -> model
|
// view -> model
|
||||||
dir.valueAccessor.registerOnChange((newValue: any) => {
|
dir.valueAccessor.registerOnChange((newValue: any) => {
|
||||||
dir.viewToModelUpdate(newValue);
|
dir.viewToModelUpdate(newValue);
|
||||||
control.updateValue(newValue, {emitModelToViewChange: false});
|
|
||||||
control.markAsDirty();
|
control.markAsDirty();
|
||||||
|
control.updateValue(newValue, {emitModelToViewChange: false});
|
||||||
});
|
});
|
||||||
|
|
||||||
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
|
control.registerOnChange((newValue: any, emitModelEvent: boolean) => {
|
||||||
|
|
|
@ -409,9 +409,9 @@ export class FormControl extends AbstractControl {
|
||||||
}
|
}
|
||||||
|
|
||||||
reset(value: any = null, {onlySelf}: {onlySelf?: boolean} = {}): void {
|
reset(value: any = null, {onlySelf}: {onlySelf?: boolean} = {}): void {
|
||||||
this.updateValue(value, {onlySelf: onlySelf});
|
|
||||||
this.markAsPristine({onlySelf: onlySelf});
|
this.markAsPristine({onlySelf: onlySelf});
|
||||||
this.markAsUntouched({onlySelf: onlySelf});
|
this.markAsUntouched({onlySelf: onlySelf});
|
||||||
|
this.updateValue(value, {onlySelf: onlySelf});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -236,6 +236,33 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should mark controls as dirty before emitting a value change event',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||||
|
const login = new FormControl('oldValue');
|
||||||
|
const form = new FormGroup({'login': login});
|
||||||
|
|
||||||
|
const t = `<div [formGroup]="form">
|
||||||
|
<input type="text" formControlName="login">
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
|
||||||
|
fixture.debugElement.componentInstance.form = form;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
login.valueChanges.subscribe(() => {
|
||||||
|
expect(login.dirty).toBe(true);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||||
|
loginEl.value = 'newValue';
|
||||||
|
|
||||||
|
dispatchEvent(loginEl, 'input');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should clear value in UI when form resets programmatically',
|
it('should clear value in UI when form resets programmatically',
|
||||||
inject(
|
inject(
|
||||||
[TestComponentBuilder, AsyncTestCompleter],
|
[TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
@ -289,6 +316,37 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should mark control as pristine before emitting a value change event when resetting ',
|
||||||
|
inject(
|
||||||
|
[TestComponentBuilder, AsyncTestCompleter],
|
||||||
|
(tcb: TestComponentBuilder, async: AsyncTestCompleter) => {
|
||||||
|
const login = new FormControl('oldValue');
|
||||||
|
const form = new FormGroup({'login': login});
|
||||||
|
|
||||||
|
const t = `<div [formGroup]="form">
|
||||||
|
<input type="text" formControlName="login">
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
|
||||||
|
fixture.debugElement.componentInstance.form = form;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||||
|
loginEl.value = 'newValue';
|
||||||
|
|
||||||
|
dispatchEvent(loginEl, 'input');
|
||||||
|
|
||||||
|
expect(login.pristine).toBe(false);
|
||||||
|
|
||||||
|
login.valueChanges.subscribe(() => {
|
||||||
|
expect(login.pristine).toBe(true);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
|
||||||
|
form.reset();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should support form arrays',
|
it('should support form arrays',
|
||||||
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]);
|
const cityArray = new FormArray([new FormControl('SF'), new FormControl('NY')]);
|
||||||
|
|
|
@ -369,6 +369,58 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should mark controls as dirty before emitting a value change event',
|
||||||
|
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
||||||
|
const t = `<form>
|
||||||
|
<input type="text" name="login" ngModel>
|
||||||
|
</form>`;
|
||||||
|
|
||||||
|
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm).form;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
form.find('login').valueChanges.subscribe(
|
||||||
|
() => { expect(form.find('login').dirty).toBe(true); });
|
||||||
|
|
||||||
|
const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||||
|
loginEl.value = 'newValue';
|
||||||
|
|
||||||
|
dispatchEvent(loginEl, 'input');
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
|
it('should mark control as pristine before emitting a value change event when resetting ',
|
||||||
|
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
||||||
|
const t = `<form>
|
||||||
|
<input type="text" name="login" ngModel>
|
||||||
|
</form>`;
|
||||||
|
|
||||||
|
tcb.overrideTemplate(MyComp8, t).createAsync(MyComp8).then((fixture) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const form = fixture.debugElement.children[0].injector.get(NgForm).form;
|
||||||
|
const formEl = fixture.debugElement.query(By.css('form')).nativeElement;
|
||||||
|
const loginEl = fixture.debugElement.query(By.css('input')).nativeElement;
|
||||||
|
fixture.detectChanges();
|
||||||
|
tick();
|
||||||
|
|
||||||
|
loginEl.value = 'newValue';
|
||||||
|
dispatchEvent(loginEl, 'input');
|
||||||
|
|
||||||
|
expect(form.find('login').pristine).toBe(false);
|
||||||
|
|
||||||
|
form.find('login').valueChanges.subscribe(
|
||||||
|
() => { expect(form.find('login').pristine).toBe(true); });
|
||||||
|
|
||||||
|
dispatchEvent(formEl, 'reset');
|
||||||
|
});
|
||||||
|
})));
|
||||||
|
|
||||||
describe('radio value accessor', () => {
|
describe('radio value accessor', () => {
|
||||||
it('should support <type=radio>',
|
it('should support <type=radio>',
|
||||||
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
fakeAsync(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||||
|
|
Loading…
Reference in New Issue