Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/material/datepicker/date-range-input-parts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ abstract class MatDateRangeInputPartBase<D>
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<D>)
? this._rangeInput._endInput
Expand Down
16 changes: 5 additions & 11 deletions src/material/datepicker/datepicker-input-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
return this._model ? this._getValueFromModel(this._model.selection) : this._pendingValue;
}
set value(value: any) {
this._assignValueProgrammatically(value, true);
this._assignValueProgrammatically(value);
}
protected _model: MatDateSelectionModel<S, D> | undefined;

Expand Down Expand Up @@ -257,7 +257,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection

// Update the displayed date when the locale changes.
this._localeSubscription = this._dateAdapter.localeChanges.subscribe(() => {
this._assignValueProgrammatically(this.value, true);
this._assignValueProgrammatically(this.value);
});
}

Expand Down Expand Up @@ -289,10 +289,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection

/** Implemented as part of ControlValueAccessor. */
writeValue(value: D): void {
// We produce a different date object on each keystroke which can cause signal forms'
// interop logic to keep calling `writeValue` with the same value as the user is typing.
// Skip such cases since they can prevent the user from typing (see #32442 and #32475).
this._assignValueProgrammatically(value, value !== this.value);
this._assignValueProgrammatically(value);
}

/** Implemented as part of ControlValueAccessor. */
Expand Down Expand Up @@ -402,15 +399,12 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
}

/** Programmatically assigns a value to the input. */
protected _assignValueProgrammatically(value: D | null, reformat: boolean) {
protected _assignValueProgrammatically(value: D | null) {
value = this._dateAdapter.deserialize(value);
this._lastValueValid = this._isValidValue(value);
value = this._dateAdapter.getValidDateOrNull(value);
this._assignValue(value);

if (reformat) {
this._formatValue(value);
}
this._formatValue(value);
}

/** Gets whether a value matches the current date filter. */
Expand Down
56 changes: 33 additions & 23 deletions src/material/datepicker/datepicker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1072,35 +1072,18 @@ describe('MatDatepicker', () => {
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);
});
});

Expand Down Expand Up @@ -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();
Expand Down
Loading