From d3de6bc712d5b5b3dd9a1eae91268e7e8203534b Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 10 Jun 2020 18:15:22 +0200 Subject: [PATCH] fix(datepicker): date range overriding model value if both fields are changed at the same time 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. --- .../datepicker/date-range-input-parts.ts | 15 ++++----------- .../datepicker/date-range-input.spec.ts | 18 ++++++++++++++++++ .../datepicker/datepicker-input-base.ts | 2 +- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/material/datepicker/date-range-input-parts.ts b/src/material/datepicker/date-range-input-parts.ts index 6d30b24e88e3..1d21399317fe 100644 --- a/src/material/datepicker/date-range-input-parts.ts +++ b/src/material/datepicker/date-range-input-parts.ts @@ -40,7 +40,7 @@ import { import {BooleanInput} from '@angular/cdk/coercion'; import {BACKSPACE} from '@angular/cdk/keycodes'; import {MatDatepickerInputBase, DateFilterFn} from './datepicker-input-base'; -import {DateRange, MatDateSelectionModel} from './date-selection-model'; +import {DateRange} from './date-selection-model'; /** Parent component that should be wrapped around `MatStartDate` and `MatEndDate`. */ export interface MatDateRangeInputParent { @@ -165,15 +165,6 @@ abstract class MatDateRangeInputPartBase protected _parentDisabled() { return this._rangeInput._groupDisabled; } - - _registerModel(model: MatDateSelectionModel, D>) { - // The very first time the range inputs write their values, they don't know about the value - // of the opposite input. When this is combined with the fact that `NgModel` defers writing - // its value with a `Promise.resolve`, we can get into a situation where the first input - // resets the value of the second. We work around it by deferring the registration of - // the model, allowing the input enough time to assign the initial value. - Promise.resolve().then(() => super._registerModel(model)); - } } const _MatDateRangeInputBase: @@ -242,6 +233,7 @@ export class MatStartDate extends _MatDateRangeInputBase implements CanUpd if (this._model) { const range = new DateRange(value, this._model.selection.end); this._model.updateSelection(range, this); + this._cvaOnChange(value); } } @@ -323,11 +315,12 @@ export class MatEndDate extends _MatDateRangeInputBase implements CanUpdat if (this._model) { const range = new DateRange(this._model.selection.start, value); this._model.updateSelection(range, this); + this._cvaOnChange(value); } } _onKeydown(event: KeyboardEvent) { - // If the user is pressing backspace on an empty end input, focus focus back to the start. + // If the user is pressing backspace on an empty end input, move focus back to the start. if (event.keyCode === BACKSPACE && !this._elementRef.nativeElement.value) { this._rangeInput._startInput.focus(); } diff --git a/src/material/datepicker/date-range-input.spec.ts b/src/material/datepicker/date-range-input.spec.ts index e6ef835d498c..7db192b519a4 100644 --- a/src/material/datepicker/date-range-input.spec.ts +++ b/src/material/datepicker/date-range-input.spec.ts @@ -475,6 +475,24 @@ describe('MatDateRangeInput', () => { expect(fixture.componentInstance.end).toBe(end); })); + it('should preserve the values when assigning both together through ngModel', fakeAsync(() => { + const assignAndAssert = (start: Date, end: Date) => { + fixture.componentInstance.start = start; + fixture.componentInstance.end = end; + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + expect(fixture.componentInstance.start).toBe(start); + expect(fixture.componentInstance.end).toBe(end); + }; + + const fixture = createComponent(RangePickerNgModel); + fixture.detectChanges(); + + assignAndAssert(new Date(2020, 1, 2), new Date(2020, 1, 5)); + assignAndAssert(new Date(2020, 2, 2), new Date(2020, 2, 5)); + })); + it('should move focus to the start input when pressing backspace on an empty end input', () => { const fixture = createComponent(StandardRangePicker); fixture.detectChanges(); diff --git a/src/material/datepicker/datepicker-input-base.ts b/src/material/datepicker/datepicker-input-base.ts index d137d69d9c22..8775f8c57f1f 100644 --- a/src/material/datepicker/datepicker-input-base.ts +++ b/src/material/datepicker/datepicker-input-base.ts @@ -125,7 +125,7 @@ export abstract class MatDatepickerInputBase {}; _validatorOnChange = () => {}; - private _cvaOnChange: (value: any) => void = () => {}; + protected _cvaOnChange: (value: any) => void = () => {}; private _valueChangesSubscription = Subscription.EMPTY; private _localeSubscription = Subscription.EMPTY;