Skip to content

Commit e4faf43

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

File tree

4 files changed

+97
-3
lines changed

4 files changed

+97
-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: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,57 @@ 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.detectChanges();
886+
tick();
887+
888+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(1, 'Start Date set once');
889+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(0, 'End Date not set');
890+
891+
fixture.componentInstance.rangePicker.select(toDate);
892+
fixture.detectChanges();
893+
tick();
894+
895+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(1, 'Start Date unchanged (set once)');
896+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(1, 'End Date set once');
897+
898+
fixture.componentInstance.rangePicker.open();
899+
fixture.detectChanges();
900+
tick();
901+
902+
const fromDate2 = new Date(2021, 0, 1);
903+
const toDate2 = new Date(2021, 0, 2);
904+
fixture.componentInstance.rangePicker.select(fromDate2);
905+
fixture.detectChanges();
906+
tick();
907+
908+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(2, 'Start Date set twice');
909+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(2, 'End Date set twice (nulled)');
910+
911+
fixture.componentInstance.rangePicker.select(toDate2);
912+
fixture.detectChanges();
913+
tick();
914+
915+
expect(fixture.componentInstance.startDateModelChangeCount).toBe(2, 'Start Date unchanged (set twice)');
916+
expect(fixture.componentInstance.endDateModelChangeCount).toBe(3, 'End date set three times');
917+
}));
918+
868919
});
869920

870921
@Component({
@@ -959,8 +1010,24 @@ class RangePickerNgModel {
9591010
@ViewChild(MatStartDate, {read: ElementRef}) startInput: ElementRef<HTMLInputElement>;
9601011
@ViewChild(MatEndDate, {read: ElementRef}) endInput: ElementRef<HTMLInputElement>;
9611012
@ViewChild(MatDateRangePicker) rangePicker: MatDateRangePicker<Date>;
962-
start: Date | null = null;
963-
end: Date | null = null;
1013+
private _start: Date|null = null;
1014+
get start(): Date|null {
1015+
return this._start;
1016+
}
1017+
set start(aStart: Date|null) {
1018+
this.startDateModelChangeCount++;
1019+
this._start = aStart;
1020+
}
1021+
private _end: Date|null = null;
1022+
get end(): Date|null {
1023+
return this._end;
1024+
}
1025+
set end(anEnd: Date|null) {
1026+
this.endDateModelChangeCount++;
1027+
this._end = anEnd;
1028+
}
1029+
startDateModelChangeCount = 0;
1030+
endDateModelChangeCount = 0;
9641031
}
9651032

9661033

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)