From 4a73f2ccfa408b770a11c8fb49293f3295a4453f Mon Sep 17 00:00:00 2001 From: Mike Calmus Date: Tue, 30 Mar 2021 08:27:17 -0400 Subject: [PATCH] fix(material/datepicker): Only update selection when value changed (#21846) --- .../datepicker/date-range-input-parts.ts | 20 +++++ .../datepicker/date-range-input.spec.ts | 74 ++++++++++++++++++- .../datepicker/date-selection-model.ts | 6 +- .../public_api_guard/material/datepicker.d.ts | 3 + 4 files changed, 100 insertions(+), 3 deletions(-) diff --git a/src/material/datepicker/date-range-input-parts.ts b/src/material/datepicker/date-range-input-parts.ts index 80638bcbf344..f9b92e555cf7 100644 --- a/src/material/datepicker/date-range-input-parts.ts +++ b/src/material/datepicker/date-range-input-parts.ts @@ -261,6 +261,16 @@ export class MatStartDate extends _MatDateRangeInputBase implements return modelValue.start; } + protected _shouldHandleChangeEvent(change: DateSelectionModelChange>): boolean { + if (!super._shouldHandleChangeEvent(change)) { + return false; + } else { + return !change.oldValue?.start ? !!change.selection.start : + !change.selection.start || + !!this._dateAdapter.compareDate(change.oldValue.start, change.selection.start); + } + } + protected _assignValueToModel(value: D | null) { if (this._model) { const range = new DateRange(value, this._model.selection.end); @@ -365,6 +375,16 @@ export class MatEndDate extends _MatDateRangeInputBase implements return modelValue.end; } + protected _shouldHandleChangeEvent(change: DateSelectionModelChange>): boolean { + if (!super._shouldHandleChangeEvent(change)) { + return false; + } else { + return !change.oldValue?.end ? !!change.selection.end : + !change.selection.end || + !!this._dateAdapter.compareDate(change.oldValue.end, change.selection.end); + } + } + protected _assignValueToModel(value: D | null) { if (this._model) { const range = new DateRange(this._model.selection.start, value); diff --git a/src/material/datepicker/date-range-input.spec.ts b/src/material/datepicker/date-range-input.spec.ts index a98fdc269144..1b8c2c095770 100644 --- a/src/material/datepicker/date-range-input.spec.ts +++ b/src/material/datepicker/date-range-input.spec.ts @@ -865,6 +865,60 @@ describe('MatDateRangeInput', () => { expect(endInput.errorStateMatcher).toBe(matcher); }); + + it('should only update model for input that changed', fakeAsync(() => { + const fixture = createComponent(RangePickerNgModel); + + fixture.detectChanges(); + tick(); + + expect(fixture.componentInstance.startDateModelChangeCount).toBe(0); + expect(fixture.componentInstance.endDateModelChangeCount).toBe(0); + + fixture.componentInstance.rangePicker.open(); + fixture.detectChanges(); + tick(); + + const fromDate = new Date(2020, 0, 1); + const toDate = new Date(2020, 0, 2); + fixture.componentInstance.rangePicker.select(fromDate); + fixture.detectChanges(); + tick(); + + expect(fixture.componentInstance.startDateModelChangeCount).toBe(1, 'Start Date set once'); + expect(fixture.componentInstance.endDateModelChangeCount).toBe(0, 'End Date not set'); + + fixture.componentInstance.rangePicker.select(toDate); + fixture.detectChanges(); + tick(); + + expect(fixture.componentInstance.startDateModelChangeCount).toBe(1, + 'Start Date unchanged (set once)'); + expect(fixture.componentInstance.endDateModelChangeCount).toBe(1, 'End Date set once'); + + fixture.componentInstance.rangePicker.open(); + fixture.detectChanges(); + tick(); + + const fromDate2 = new Date(2021, 0, 1); + const toDate2 = new Date(2021, 0, 2); + fixture.componentInstance.rangePicker.select(fromDate2); + fixture.detectChanges(); + tick(); + + expect(fixture.componentInstance.startDateModelChangeCount).toBe(2, 'Start Date set twice'); + expect(fixture.componentInstance.endDateModelChangeCount).toBe(2, + 'End Date set twice (nulled)'); + + fixture.componentInstance.rangePicker.select(toDate2); + fixture.detectChanges(); + tick(); + + expect(fixture.componentInstance.startDateModelChangeCount).toBe(2, + 'Start Date unchanged (set twice)'); + expect(fixture.componentInstance.endDateModelChangeCount).toBe(3, 'End date set three times'); + })); + }); @Component({ @@ -959,8 +1013,24 @@ class RangePickerNgModel { @ViewChild(MatStartDate, {read: ElementRef}) startInput: ElementRef; @ViewChild(MatEndDate, {read: ElementRef}) endInput: ElementRef; @ViewChild(MatDateRangePicker) rangePicker: MatDateRangePicker; - start: Date | null = null; - end: Date | null = null; + private _start: Date|null = null; + get start(): Date|null { + return this._start; + } + set start(aStart: Date|null) { + this.startDateModelChangeCount++; + this._start = aStart; + } + private _end: Date|null = null; + get end(): Date|null { + return this._end; + } + set end(anEnd: Date|null) { + this.endDateModelChangeCount++; + this._end = anEnd; + } + startDateModelChangeCount = 0; + endDateModelChangeCount = 0; } diff --git a/src/material/datepicker/date-selection-model.ts b/src/material/datepicker/date-selection-model.ts index 637213b10d99..165188d10061 100644 --- a/src/material/datepicker/date-selection-model.ts +++ b/src/material/datepicker/date-selection-model.ts @@ -42,6 +42,9 @@ export interface DateSelectionModelChange { /** Object that triggered the change. */ source: unknown; + + /** Previous value */ + oldValue?: S; } /** @@ -69,8 +72,9 @@ export abstract class MatDateSelectionModel { } export interface DateSelectionModelChange { + oldValue?: S; selection: S; source: unknown; } @@ -406,6 +407,7 @@ export declare class MatEndDate extends _MatDateRangeInputBase implements protected _assignValueToModel(value: D | null): void; protected _getValueFromModel(modelValue: DateRange): D | null; _onKeydown(event: KeyboardEvent): void; + protected _shouldHandleChangeEvent(change: DateSelectionModelChange>): boolean; ngDoCheck(): void; ngOnInit(): void; static ngAcceptInputType_disabled: BooleanInput; @@ -517,6 +519,7 @@ export declare class MatStartDate extends _MatDateRangeInputBase implement protected _assignValueToModel(value: D | null): void; protected _formatValue(value: D | null): void; protected _getValueFromModel(modelValue: DateRange): D | null; + protected _shouldHandleChangeEvent(change: DateSelectionModelChange>): boolean; getMirrorValue(): string; ngDoCheck(): void; ngOnInit(): void;