Skip to content

Commit f428c00

Browse files
authored
fix(datepicker): date range overriding model value if both fields are changed at the same time (#19593)
The date range picker inputs are set up so that they only respond to events outside of themselves so that we don't trigger duplicate events. In some cases this can be a problem, because the input can end up ignoring its own call to update the CVA value, causing it to be out of sync. These changes add an extra call that ensures that the model and CVA values are always in sync. Fixes #19588.
1 parent e208197 commit f428c00

File tree

3 files changed

+23
-12
lines changed

3 files changed

+23
-12
lines changed

src/material/datepicker/date-range-input-parts.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import {
4040
import {BooleanInput} from '@angular/cdk/coercion';
4141
import {BACKSPACE} from '@angular/cdk/keycodes';
4242
import {MatDatepickerInputBase, DateFilterFn} from './datepicker-input-base';
43-
import {DateRange, MatDateSelectionModel} from './date-selection-model';
43+
import {DateRange} from './date-selection-model';
4444

4545
/** Parent component that should be wrapped around `MatStartDate` and `MatEndDate`. */
4646
export interface MatDateRangeInputParent<D> {
@@ -170,15 +170,6 @@ abstract class MatDateRangeInputPartBase<D>
170170
protected _parentDisabled() {
171171
return this._rangeInput._groupDisabled;
172172
}
173-
174-
_registerModel(model: MatDateSelectionModel<DateRange<D>, D>) {
175-
// The very first time the range inputs write their values, they don't know about the value
176-
// of the opposite input. When this is combined with the fact that `NgModel` defers writing
177-
// its value with a `Promise.resolve`, we can get into a situation where the first input
178-
// resets the value of the second. We work around it by deferring the registration of
179-
// the model, allowing the input enough time to assign the initial value.
180-
Promise.resolve().then(() => super._registerModel(model));
181-
}
182173
}
183174

184175
const _MatDateRangeInputBase:
@@ -247,6 +238,7 @@ export class MatStartDate<D> extends _MatDateRangeInputBase<D> implements CanUpd
247238
if (this._model) {
248239
const range = new DateRange(value, this._model.selection.end);
249240
this._model.updateSelection(range, this);
241+
this._cvaOnChange(value);
250242
}
251243
}
252244

@@ -328,11 +320,12 @@ export class MatEndDate<D> extends _MatDateRangeInputBase<D> implements CanUpdat
328320
if (this._model) {
329321
const range = new DateRange(this._model.selection.start, value);
330322
this._model.updateSelection(range, this);
323+
this._cvaOnChange(value);
331324
}
332325
}
333326

334327
_onKeydown(event: KeyboardEvent) {
335-
// If the user is pressing backspace on an empty end input, focus focus back to the start.
328+
// If the user is pressing backspace on an empty end input, move focus back to the start.
336329
if (event.keyCode === BACKSPACE && !this._elementRef.nativeElement.value) {
337330
this._rangeInput._startInput.focus();
338331
}

src/material/datepicker/date-range-input.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,24 @@ describe('MatDateRangeInput', () => {
475475
expect(fixture.componentInstance.end).toBe(end);
476476
}));
477477

478+
it('should preserve the values when assigning both together through ngModel', fakeAsync(() => {
479+
const assignAndAssert = (start: Date, end: Date) => {
480+
fixture.componentInstance.start = start;
481+
fixture.componentInstance.end = end;
482+
fixture.detectChanges();
483+
tick();
484+
fixture.detectChanges();
485+
expect(fixture.componentInstance.start).toBe(start);
486+
expect(fixture.componentInstance.end).toBe(end);
487+
};
488+
489+
const fixture = createComponent(RangePickerNgModel);
490+
fixture.detectChanges();
491+
492+
assignAndAssert(new Date(2020, 1, 2), new Date(2020, 1, 5));
493+
assignAndAssert(new Date(2020, 2, 2), new Date(2020, 2, 5));
494+
}));
495+
478496
it('should move focus to the start input when pressing backspace on an empty end input', () => {
479497
const fixture = createComponent(StandardRangePicker);
480498
fixture.detectChanges();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ export abstract class MatDatepickerInputBase<S, D = ExtractDateTypeFromSelection
125125
_onTouched = () => {};
126126
_validatorOnChange = () => {};
127127

128-
private _cvaOnChange: (value: any) => void = () => {};
128+
protected _cvaOnChange: (value: any) => void = () => {};
129129
private _valueChangesSubscription = Subscription.EMPTY;
130130
private _localeSubscription = Subscription.EMPTY;
131131

0 commit comments

Comments
 (0)