Skip to content

Commit

Permalink
fix(material/datepicker): avoid rerender when min/maxDate changes to …
Browse files Browse the repository at this point in the history
…different time on the same day (#24434)

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
  • Loading branch information
zarend authored Mar 31, 2022
1 parent b1d20f9 commit 6f9743c
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
29 changes: 29 additions & 0 deletions src/material/datepicker/calendar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,16 @@ describe('MatCalendar', () => {
expect(calendarInstance.monthView._init).toHaveBeenCalled();
});

it('should not re-render the month view when the minDate changes to the same day at a different time', () => {
fixture.detectChanges();
spyOn(calendarInstance.monthView, '_init').and.callThrough();

testComponent.minDate = new Date(2016, JAN, 1, 0, 0, 0, 1);
fixture.detectChanges();

expect(calendarInstance.monthView._init).not.toHaveBeenCalled();
});

it('should re-render the month view when the maxDate changes', () => {
fixture.detectChanges();
spyOn(calendarInstance.monthView, '_init').and.callThrough();
Expand Down Expand Up @@ -479,6 +489,25 @@ describe('MatCalendar', () => {
expect(calendarInstance.yearView._init).toHaveBeenCalled();
});

it('should not re-render the year view when the maxDate changes to the same day at a different time', () => {
fixture.detectChanges();
const periodButton = calendarElement.querySelector(
'.mat-calendar-period-button',
) as HTMLElement;
periodButton.click();
fixture.detectChanges();

(calendarElement.querySelector('.mat-calendar-body-active') as HTMLElement).click();
fixture.detectChanges();

spyOn(calendarInstance.yearView, '_init').and.callThrough();

testComponent.maxDate = new Date(2018, JAN, 1, 0, 0, 1, 0);
fixture.detectChanges();

expect(calendarInstance.yearView._init).not.toHaveBeenCalled();
});

it('should re-render the multi-year view when the minDate changes', () => {
fixture.detectChanges();
const periodButton = calendarElement.querySelector(
Expand Down
17 changes: 16 additions & 1 deletion src/material/datepicker/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
OnDestroy,
Optional,
Output,
SimpleChange,
SimpleChanges,
ViewChild,
ViewEncapsulation,
Expand Down Expand Up @@ -393,7 +394,21 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
}

ngOnChanges(changes: SimpleChanges) {
const change = changes['minDate'] || changes['maxDate'] || changes['dateFilter'];
// Ignore date changes that are at a different time on the same day. This fixes issues where
// the calendar re-renders when there is no meaningful change to [minDate] or [maxDate]
// (#24435).
const minDateChange: SimpleChange | undefined =
changes['minDate'] &&
!this._dateAdapter.sameDate(changes['minDate'].previousValue, changes['minDate'].currentValue)
? changes['minDate']
: undefined;
const maxDateChange: SimpleChange | undefined =
changes['maxDate'] &&
!this._dateAdapter.sameDate(changes['maxDate'].previousValue, changes['maxDate'].currentValue)
? changes['maxDate']
: undefined;

const change = minDateChange || maxDateChange || changes['dateFilter'];

if (change && !change.firstChange) {
const view = this._getCurrentViewComponent();
Expand Down

0 comments on commit 6f9743c

Please sign in to comment.