Skip to content

Commit 4cc5880

Browse files
authored
fix(material/datepicker): do not re-assign the same forms value (#32447)
The datepicker ControlValueAccessor produces a new date object for each keystroke as the user is typing. When used with the interop behavior from signal forms, this can end up reverting the user's value as they're typing. These changes ignore `writeValue` calls if they pass the exact same date object. Fixes #32442.
1 parent 8e8314c commit 4cc5880

File tree

2 files changed

+26
-5
lines changed

2 files changed

+26
-5
lines changed

src/material/datepicker/datepicker-input-base.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,22 +289,27 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
289289
return this._validator ? this._validator(c) : null;
290290
}
291291

292-
// Implemented as part of ControlValueAccessor.
292+
/** Implemented as part of ControlValueAccessor. */
293293
writeValue(value: D): void {
294-
this._assignValueProgrammatically(value);
294+
// We produce a different date object on each keystroke which can cause signal forms'
295+
// interop logic to keep calling `writeValue` with the same value as the user is typing.
296+
// Skip such cases since they can prevent the user from typing (see #32442).
297+
if (!value || value !== this.value) {
298+
this._assignValueProgrammatically(value);
299+
}
295300
}
296301

297-
// Implemented as part of ControlValueAccessor.
302+
/** Implemented as part of ControlValueAccessor. */
298303
registerOnChange(fn: (value: any) => void): void {
299304
this._cvaOnChange = fn;
300305
}
301306

302-
// Implemented as part of ControlValueAccessor.
307+
/** Implemented as part of ControlValueAccessor. */
303308
registerOnTouched(fn: () => void): void {
304309
this._onTouched = fn;
305310
}
306311

307-
// Implemented as part of ControlValueAccessor.
312+
/** Implemented as part of ControlValueAccessor. */
308313
setDisabledState(isDisabled: boolean): void {
309314
this.disabled = isDisabled;
310315
}

src/material/datepicker/datepicker.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,22 @@ describe('MatDatepicker', () => {
11601160

11611161
expect(formControl.hasError('matDatepickerParse')).toBe(true);
11621162
});
1163+
1164+
it('should not re-format the input value if the forms module re-assigns the same date', () => {
1165+
const input = fixture.nativeElement.querySelector('input');
1166+
const date = new Date(2017, JAN, 1);
1167+
testComponent.formControl.setValue(date);
1168+
fixture.detectChanges();
1169+
expect(input.value).toContain('2017');
1170+
1171+
// Note: this isn't how users would behave, but it captures
1172+
// the sequence of events with signal forms.
1173+
input.value = 'foo';
1174+
testComponent.formControl.setValue(date);
1175+
fixture.detectChanges();
1176+
1177+
expect(input.value).toBe('foo');
1178+
});
11631179
});
11641180

11651181
describe('datepicker with mat-datepicker-toggle', () => {

0 commit comments

Comments
 (0)