diff --git a/packages/forms/src/directives/shared.ts b/packages/forms/src/directives/shared.ts index c261a9a44d989..0d853f84fcfa1 100644 --- a/packages/forms/src/directives/shared.ts +++ b/packages/forms/src/directives/shared.ts @@ -99,9 +99,9 @@ function setUpBlurPipeline(control: FormControl, dir: NgControl): void { } function updateControl(control: FormControl, dir: NgControl): void { - dir.viewToModelUpdate(control._pendingValue); if (control._pendingDirty) control.markAsDirty(); control.setValue(control._pendingValue, {emitModelToViewChange: false}); + dir.viewToModelUpdate(control._pendingValue); control._pendingChange = false; } diff --git a/packages/forms/test/template_integration_spec.ts b/packages/forms/test/template_integration_spec.ts index 7426b74fda937..fa75d90b11f2c 100644 --- a/packages/forms/test/template_integration_spec.ts +++ b/packages/forms/test/template_integration_spec.ts @@ -1465,6 +1465,36 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat expect(required.nativeElement.getAttribute('pattern')).toEqual(null); })); + it('should update control status', fakeAsync(() => { + const fixture = initTest(NgModelChangeState); + const inputEl = fixture.debugElement.query(By.css('input')); + const inputNativeEl = inputEl.nativeElement; + const onNgModelChange = jasmine.createSpy('onNgModelChange'); + fixture.componentInstance.onNgModelChange = onNgModelChange; + fixture.detectChanges(); + tick(); + + expect(onNgModelChange).not.toHaveBeenCalled(); + + inputNativeEl.value = 'updated'; + onNgModelChange.and.callFake((ngModel: NgModel) => { + expect(ngModel.invalid).toBe(true); + expect(ngModel.value).toBe('updated'); + }); + dispatchEvent(inputNativeEl, 'input'); + expect(onNgModelChange).toHaveBeenCalled(); + tick(); + + inputNativeEl.value = '333'; + onNgModelChange.and.callFake((ngModel: NgModel) => { + expect(ngModel.invalid).toBe(false); + expect(ngModel.value).toBe('333'); + }); + dispatchEvent(inputNativeEl, 'input'); + expect(onNgModelChange).toHaveBeenCalledTimes(2); + tick(); + })); + }); describe('IME events', () => { @@ -1802,6 +1832,17 @@ class NgModelChangesForm { log() { this.events.push('fired'); } } +@Component({ + selector: 'ng-model-change-state', + template: ` + + ` +}) +class NgModelChangeState { + onNgModelChange = () => {}; +} + function sortedClassList(el: HTMLElement) { const l = getDOM().classList(el); l.sort();