From bec59a061c1f97c771c919b8dadca57a1d4e48c3 Mon Sep 17 00:00:00 2001 From: Arul1998 Date: Tue, 20 Oct 2026 21:45:39 +0100 Subject: [PATCH] fix(material/datepicker): clear invalid text when the form value is reset `writeValue` skipped re-formatting the input when the incoming value was the same as the current one. This was added in #32447 and #32485 to work around signal forms writing the same value back into the CVA while the user was typing (#32442, #32475). As a side effect, calling `reset()` or `setValue(null)` after the user typed an unparseable date left the invalid text in the input without a parse error. Signal forms `reset()` was affected as well. Angular 22.0.0 deduplicates `writeValue` calls in the CVA interop (angular/angular@2e9aeea0f), so the workaround is no longer necessary. These changes remove it so that `writeValue` always re-formats the input. Fixes #33978. --- .../datepicker/date-range-input-parts.ts | 4 +- .../datepicker/datepicker-input-base.ts | 16 ++---- src/material/datepicker/datepicker.spec.ts | 56 +++++++++++-------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/material/datepicker/date-range-input-parts.ts b/src/material/datepicker/date-range-input-parts.ts index 62c2b810088c..9371ade07dee 100644 --- a/src/material/datepicker/date-range-input-parts.ts +++ b/src/material/datepicker/date-range-input-parts.ts @@ -190,8 +190,8 @@ abstract class MatDateRangeInputPartBase return source !== this._rangeInput._startInput && source !== this._rangeInput._endInput; } - protected override _assignValueProgrammatically(value: D | null, reformat: boolean) { - super._assignValueProgrammatically(value, reformat); + protected override _assignValueProgrammatically(value: D | null) { + super._assignValueProgrammatically(value); const opposite = ( this === (this._rangeInput._startInput as MatDateRangeInputPartBase) ? this._rangeInput._endInput diff --git a/src/material/datepicker/datepicker-input-base.ts b/src/material/datepicker/datepicker-input-base.ts index 6b7d90a12007..9463fc8e5caa 100644 --- a/src/material/datepicker/datepicker-input-base.ts +++ b/src/material/datepicker/datepicker-input-base.ts @@ -94,7 +94,7 @@ export abstract class MatDatepickerInputBase | undefined; @@ -257,7 +257,7 @@ export abstract class MatDatepickerInputBase { - this._assignValueProgrammatically(this.value, true); + this._assignValueProgrammatically(this.value); }); } @@ -289,10 +289,7 @@ export abstract class MatDatepickerInputBase { expect(formControl.hasError('matDatepickerParse')).toBe(true); }); - it('should not re-format the input value if the forms module re-assigns the same date', () => { + it('should clear an invalid value when the control is reset', () => { const input = fixture.nativeElement.querySelector('input'); - const date = new Date(2017, JAN, 1); - testComponent.formControl.setValue(date); - fixture.detectChanges(); - expect(input.value).toContain('2017'); - - // Note: this isn't how users would behave, but it captures - // the sequence of events with signal forms. - input.value = 'foo'; - testComponent.formControl.setValue(date); + typeInElement(input, 'foo'); fixture.detectChanges(); - + expect(testComponent.formControl.value).toBeNull(); expect(input.value).toBe('foo'); - }); - - it('should not re-format the input value if the forms module re-assigns null', () => { - const input = fixture.nativeElement.querySelector('input'); - testComponent.formControl.setValue(null); - fixture.detectChanges(); - expect(input.value).toBe(''); - // Note: this isn't how users would behave, but it captures - // the sequence of events with signal forms. - input.value = 'foo'; - testComponent.formControl.setValue(null); + testComponent.formControl.reset(); fixture.detectChanges(); - expect(input.value).toBe('foo'); + expect(input.value).toBe(''); + expect(testComponent.formControl.hasError('matDatepickerParse')).toBe(false); }); }); @@ -1130,6 +1113,33 @@ describe('MatDatepicker', () => { expect(hasParseError()).toBe(true); }); + it('should not clear an invalid value while the user is typing', () => { + typeInElement(input, 'foo'); + fixture.detectChanges(); + + expect(input.value).toBe('foo'); + expect(fixture.componentInstance.dateForm().value()).toBeNull(); + }); + + it('should not re-format a valid value while the user is typing', () => { + typeInElement(input, '01/01/2017'); + fixture.detectChanges(); + + expect(input.value).toBe('01/01/2017'); + expect(fixture.componentInstance.dateForm().value()).toEqual(new Date(2017, JAN, 1)); + }); + + it('should clear an invalid value when the form is reset', () => { + typeInElement(input, 'foo'); + fixture.detectChanges(); + expect(input.value).toBe('foo'); + + fixture.componentInstance.dateForm().reset(); + fixture.detectChanges(); + + expect(input.value).toBe(''); + }); + it('should clear the matDatepickerParse error when an invalid value is erased', () => { fixture.componentInstance.dateForm().value.set(new Date(2017, JAN, 1)); fixture.detectChanges();