Skip to content

Commit 67a3e81

Browse files
committed
fix(material/datepicker): Only update selection when value changed (#21846)
1 parent 7cc42f5 commit 67a3e81

File tree

4 files changed

+85
-3
lines changed

4 files changed

+85
-3
lines changed

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@ export class MatStartDate<D> extends _MatDateRangeInputBase<D> implements
261261
return modelValue.start;
262262
}
263263

264+
protected _shouldHandleChangeEvent(change: DateSelectionModelChange<DateRange<D>>): boolean {
265+
if (!super._shouldHandleChangeEvent(change)) {
266+
return false;
267+
} else {
268+
return !change.oldValue?.start ? !!change.selection.start :
269+
!change.selection.start ||
270+
!!this._dateAdapter.compareDate(change.oldValue.start, change.selection.start);
271+
}
272+
}
273+
264274
protected _assignValueToModel(value: D | null) {
265275
if (this._model) {
266276
const range = new DateRange(value, this._model.selection.end);
@@ -365,6 +375,16 @@ export class MatEndDate<D> extends _MatDateRangeInputBase<D> implements
365375
return modelValue.end;
366376
}
367377

378+
protected _shouldHandleChangeEvent(change: DateSelectionModelChange<DateRange<D>>): boolean {
379+
if (!super._shouldHandleChangeEvent(change)) {
380+
return false;
381+
} else {
382+
return !change.oldValue?.end ? !!change.selection.end :
383+
!change.selection.end ||
384+
!!this._dateAdapter.compareDate(change.oldValue.end, change.selection.end);
385+
}
386+
}
387+
368388
protected _assignValueToModel(value: D | null) {
369389
if (this._model) {
370390
const range = new DateRange(this._model.selection.start, value);

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

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,45 @@ describe('MatDateRangeInput', () => {
865865
expect(endInput.errorStateMatcher).toBe(matcher);
866866
});
867867

868+
869+
it('should only update model for input that changed', fakeAsync(() => {
870+
const fixture = createComponent(RangePickerNgModel);
871+
872+
fixture.detectChanges();
873+
tick();
874+
875+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(0);
876+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(0);
877+
878+
fixture.componentInstance.rangePicker.open();
879+
fixture.detectChanges();
880+
tick();
881+
882+
const fromDate = new Date(2020, 0, 1);
883+
const toDate = new Date(2020, 0, 2);
884+
fixture.componentInstance.rangePicker.select(fromDate);
885+
fixture.componentInstance.rangePicker.select(toDate);
886+
fixture.detectChanges();
887+
tick();
888+
889+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(1, 'Start Date 1');
890+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(1, 'End date 1');
891+
892+
fixture.componentInstance.rangePicker.open();
893+
fixture.detectChanges();
894+
tick();
895+
896+
const fromDate2 = new Date(2021, 0, 1);
897+
const toDate2 = new Date(2021, 0, 2);
898+
fixture.componentInstance.rangePicker.select(fromDate2);
899+
fixture.componentInstance.rangePicker.select(toDate2);
900+
fixture.detectChanges();
901+
tick();
902+
903+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(2, 'Start Date 2');
904+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(3, 'End date 2');
905+
}));
906+
868907
});
869908

870909
@Component({
@@ -959,8 +998,24 @@ class RangePickerNgModel {
959998
@ViewChild(MatStartDate, {read: ElementRef}) startInput: ElementRef<HTMLInputElement>;
960999
@ViewChild(MatEndDate, {read: ElementRef}) endInput: ElementRef<HTMLInputElement>;
9611000
@ViewChild(MatDateRangePicker) rangePicker: MatDateRangePicker<Date>;
962-
start: Date | null = null;
963-
end: Date | null = null;
1001+
private _start: Date|null = null;
1002+
set start(aStart: Date|null) {
1003+
this.startDateModelChangeCount++;
1004+
this._start = aStart;
1005+
}
1006+
get start(): Date|null {
1007+
return this._start;
1008+
}
1009+
private _end: Date|null = null;
1010+
set end(anEnd: Date|null) {
1011+
this.endDateModelChangeCount++;
1012+
this._end = anEnd;
1013+
}
1014+
get end(): Date|null {
1015+
return this._end;
1016+
}
1017+
startDateModelChangeCount = 0;
1018+
endDateModelChangeCount = 0;
9641019
}
9651020

9661021

src/material/datepicker/date-selection-model.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ export interface DateSelectionModelChange<S> {
4242

4343
/** Object that triggered the change. */
4444
source: unknown;
45+
46+
/** Previous value */
47+
oldValue?: S;
4548
}
4649

4750
/**
@@ -69,8 +72,9 @@ export abstract class MatDateSelectionModel<S, D = ExtractDateTypeFromSelection<
6972
* @param source Object that triggered the selection change.
7073
*/
7174
updateSelection(value: S, source: unknown) {
75+
const oldValue = (this as {selection: S}).selection;
7276
(this as {selection: S}).selection = value;
73-
this._selectionChanged.next({selection: value, source});
77+
this._selectionChanged.next({selection: value, source, oldValue});
7478
}
7579

7680
ngOnDestroy() {

tools/public_api_guard/material/datepicker.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export declare class DateRange<D> {
1313
}
1414

1515
export interface DateSelectionModelChange<S> {
16+
oldValue?: S;
1617
selection: S;
1718
source: unknown;
1819
}
@@ -405,6 +406,7 @@ export declare class MatEndDate<D> extends _MatDateRangeInputBase<D> implements
405406
protected _assignValueToModel(value: D | null): void;
406407
protected _getValueFromModel(modelValue: DateRange<D>): D | null;
407408
_onKeydown(event: KeyboardEvent): void;
409+
protected _shouldHandleChangeEvent(change: DateSelectionModelChange<DateRange<D>>): boolean;
408410
ngDoCheck(): void;
409411
ngOnInit(): void;
410412
static ngAcceptInputType_disabled: BooleanInput;
@@ -516,6 +518,7 @@ export declare class MatStartDate<D> extends _MatDateRangeInputBase<D> implement
516518
protected _assignValueToModel(value: D | null): void;
517519
protected _formatValue(value: D | null): void;
518520
protected _getValueFromModel(modelValue: DateRange<D>): D | null;
521+
protected _shouldHandleChangeEvent(change: DateSelectionModelChange<DateRange<D>>): boolean;
519522
getMirrorValue(): string;
520523
ngDoCheck(): void;
521524
ngOnInit(): void;

0 commit comments

Comments
 (0)