Skip to content

Commit 9f1fed6

Browse files
committed
fix(material/datepicker): avoid rerender when min/maxDate changes to different time on the same day
Avoid re-rendering <mat-calendar/> when the [minDate] or [maxDate] Input change to a different time on the same day. In `ngOnChanges`, do not call `init` if the previous and current value for minDate/maxDate are on the same day. This makes #24384 a non-breaking code change. Fixes #24435
1 parent b4058d7 commit 9f1fed6

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

src/material/datepicker/calendar.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,16 @@ describe('MatCalendar', () => {
433433
expect(calendarInstance.monthView._init).toHaveBeenCalled();
434434
});
435435

436+
it('should not re-render the month view when the minDate changes to the same day at a different time', () => {
437+
fixture.detectChanges();
438+
spyOn(calendarInstance.monthView, '_init').and.callThrough();
439+
440+
testComponent.minDate = new Date(2016, JAN, 1, 0, 0, 0, 1);
441+
fixture.detectChanges();
442+
443+
expect(calendarInstance.monthView._init).not.toHaveBeenCalled();
444+
});
445+
436446
it('should re-render the month view when the maxDate changes', () => {
437447
fixture.detectChanges();
438448
spyOn(calendarInstance.monthView, '_init').and.callThrough();
@@ -481,6 +491,25 @@ describe('MatCalendar', () => {
481491
expect(calendarInstance.yearView._init).toHaveBeenCalled();
482492
});
483493

494+
it('should not re-render the year view when the maxDate changes to the same day at a different time', () => {
495+
fixture.detectChanges();
496+
const periodButton = calendarElement.querySelector(
497+
'.mat-calendar-period-button',
498+
) as HTMLElement;
499+
periodButton.click();
500+
fixture.detectChanges();
501+
502+
(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
503+
fixture.detectChanges();
504+
505+
spyOn(calendarInstance.yearView, '_init').and.callThrough();
506+
507+
testComponent.maxDate = new Date(2018, JAN, 1, 0, 0, 1, 0);
508+
fixture.detectChanges();
509+
510+
expect(calendarInstance.yearView._init).not.toHaveBeenCalled();
511+
});
512+
484513
it('should re-render the multi-year view when the minDate changes', () => {
485514
fixture.detectChanges();
486515
const periodButton = calendarElement.querySelector(

src/material/datepicker/calendar.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
OnDestroy,
2222
Optional,
2323
Output,
24+
SimpleChange,
2425
SimpleChanges,
2526
ViewChild,
2627
ViewEncapsulation,
@@ -393,7 +394,21 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
393394
}
394395

395396
ngOnChanges(changes: SimpleChanges) {
396-
const change = changes['minDate'] || changes['maxDate'] || changes['dateFilter'];
397+
// Ignore date changes that are at a different time on the same day. This fixes issues where
398+
// the calendar re-renders when there is no meaningful change to [minDate] or [maxDate]
399+
// (#24435).
400+
const minDateChange: SimpleChange | undefined =
401+
changes['minDate'] &&
402+
!this._dateAdapter.sameDate(changes['minDate'].previousValue, changes['minDate'].currentValue)
403+
? changes['minDate']
404+
: undefined;
405+
const maxDateChange: SimpleChange | undefined =
406+
changes['maxDate'] &&
407+
!this._dateAdapter.sameDate(changes['maxDate'].previousValue, changes['maxDate'].currentValue)
408+
? changes['maxDate']
409+
: undefined;
410+
411+
const change = minDateChange || maxDateChange || changes['dateFilter'];
397412

398413
if (change && !change.firstChange) {
399414
const view = this._getCurrentViewComponent();

0 commit comments

Comments
 (0)