Skip to content

Commit 3e6a86f

Browse files
ppham27vicb
authored andcommitted
fix(forms): set state before emitting a value from ngModelChange (#21514)
Closes #21513. PR Close #21514
1 parent a7ebf5a commit 3e6a86f

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

packages/forms/src/directives/shared.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ function setUpBlurPipeline(control: FormControl, dir: NgControl): void {
9999
}
100100

101101
function updateControl(control: FormControl, dir: NgControl): void {
102-
dir.viewToModelUpdate(control._pendingValue);
103102
if (control._pendingDirty) control.markAsDirty();
104103
control.setValue(control._pendingValue, {emitModelToViewChange: false});
104+
dir.viewToModelUpdate(control._pendingValue);
105105
control._pendingChange = false;
106106
}
107107

packages/forms/test/template_integration_spec.ts

+41
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,36 @@ import {NgModelCustomComp, NgModelCustomWrapper} from './value_accessor_integrat
14721472
expect(required.nativeElement.getAttribute('pattern')).toEqual(null);
14731473
}));
14741474

1475+
it('should update control status', fakeAsync(() => {
1476+
const fixture = initTest(NgModelChangeState);
1477+
const inputEl = fixture.debugElement.query(By.css('input'));
1478+
const inputNativeEl = inputEl.nativeElement;
1479+
const onNgModelChange = jasmine.createSpy('onNgModelChange');
1480+
fixture.componentInstance.onNgModelChange = onNgModelChange;
1481+
fixture.detectChanges();
1482+
tick();
1483+
1484+
expect(onNgModelChange).not.toHaveBeenCalled();
1485+
1486+
inputNativeEl.value = 'updated';
1487+
onNgModelChange.and.callFake((ngModel: NgModel) => {
1488+
expect(ngModel.invalid).toBe(true);
1489+
expect(ngModel.value).toBe('updated');
1490+
});
1491+
dispatchEvent(inputNativeEl, 'input');
1492+
expect(onNgModelChange).toHaveBeenCalled();
1493+
tick();
1494+
1495+
inputNativeEl.value = '333';
1496+
onNgModelChange.and.callFake((ngModel: NgModel) => {
1497+
expect(ngModel.invalid).toBe(false);
1498+
expect(ngModel.value).toBe('333');
1499+
});
1500+
dispatchEvent(inputNativeEl, 'input');
1501+
expect(onNgModelChange).toHaveBeenCalledTimes(2);
1502+
tick();
1503+
}));
1504+
14751505
});
14761506

14771507
describe('IME events', () => {
@@ -1809,6 +1839,17 @@ class NgModelChangesForm {
18091839
log() { this.events.push('fired'); }
18101840
}
18111841

1842+
@Component({
1843+
selector: 'ng-model-change-state',
1844+
template: `
1845+
<input #ngModel="ngModel" ngModel [maxlength]="4"
1846+
(ngModelChange)="onNgModelChange(ngModel)">
1847+
`
1848+
})
1849+
class NgModelChangeState {
1850+
onNgModelChange = () => {};
1851+
}
1852+
18121853
function sortedClassList(el: HTMLElement) {
18131854
const l = getDOM().classList(el);
18141855
l.sort();

0 commit comments

Comments
 (0)