Skip to content

Commit b1d4fe1

Browse files
crisbetojelbourn
authored andcommitted
fix(datepicker): calendar input changes not being propagated on child views (#12004)
Fixes the new values for the calendar's `minDate`, `maxDate` and `dateFilter` not being propagated to the current calendar view until the next change detection cycle. Fixes #11737.
1 parent 32da038 commit b1d4fe1

File tree

2 files changed

+63
-3
lines changed

2 files changed

+63
-3
lines changed

src/lib/datepicker/calendar.spec.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from '@angular/cdk/testing';
1111
import {Component, NgZone} from '@angular/core';
1212
import {ComponentFixture, TestBed, async, inject} from '@angular/core/testing';
13-
import {DEC, FEB, JAN, MatNativeDateModule, NOV} from '@angular/material/core';
13+
import {DEC, FEB, JAN, MatNativeDateModule, NOV, JUL} from '@angular/material/core';
1414
import {By} from '@angular/platform-browser';
1515
import {Direction, Directionality} from '@angular/cdk/bidi';
1616
import {MatCalendar} from './calendar';
@@ -32,6 +32,7 @@ describe('MatCalendar', () => {
3232
StandardCalendar,
3333
CalendarWithMinMax,
3434
CalendarWithDateFilter,
35+
CalendarWithSelectableMinDate,
3536
],
3637
providers: [
3738
MatDatepickerIntl,
@@ -399,6 +400,37 @@ describe('MatCalendar', () => {
399400

400401
expect(calendarInstance.multiYearView._init).toHaveBeenCalled();
401402
});
403+
404+
it('should update the minDate in the child view if it changed after an interaction', () => {
405+
fixture.destroy();
406+
407+
const dynamicFixture = TestBed.createComponent(CalendarWithSelectableMinDate);
408+
dynamicFixture.detectChanges();
409+
410+
const calendarDebugElement = dynamicFixture.debugElement.query(By.directive(MatCalendar));
411+
const disabledClass = 'mat-calendar-body-disabled';
412+
calendarElement = calendarDebugElement.nativeElement;
413+
calendarInstance = calendarDebugElement.componentInstance;
414+
415+
let cells = Array.from(calendarElement.querySelectorAll('.mat-calendar-body-cell'));
416+
417+
expect(cells.slice(0, 9).every(c => c.classList.contains(disabledClass)))
418+
.toBe(true, 'Expected dates up to the 10th to be disabled.');
419+
420+
expect(cells.slice(9).every(c => c.classList.contains(disabledClass)))
421+
.toBe(false, 'Expected dates after the 10th to be enabled.');
422+
423+
(cells[14] as HTMLElement).click();
424+
dynamicFixture.detectChanges();
425+
cells = Array.from(calendarElement.querySelectorAll('.mat-calendar-body-cell'));
426+
427+
expect(cells.slice(0, 14).every(c => c.classList.contains(disabledClass)))
428+
.toBe(true, 'Expected dates up to the 14th to be disabled.');
429+
430+
expect(cells.slice(14).every(c => c.classList.contains(disabledClass)))
431+
.toBe(false, 'Expected dates after the 14th to be enabled.');
432+
});
433+
402434
});
403435

404436
describe('calendar with date filter', () => {
@@ -521,3 +553,28 @@ class CalendarWithDateFilter {
521553
return !(date.getDate() % 2) && date.getMonth() !== NOV;
522554
}
523555
}
556+
557+
558+
@Component({
559+
template: `
560+
<mat-calendar
561+
[startAt]="startAt"
562+
(selectedChange)="select($event)"
563+
[selected]="selected"
564+
[minDate]="selected">
565+
</mat-calendar>
566+
`
567+
})
568+
class CalendarWithSelectableMinDate {
569+
startAt = new Date(2018, JUL, 0);
570+
selected: Date;
571+
minDate: Date;
572+
573+
constructor() {
574+
this.select(new Date(2018, JUL, 10));
575+
}
576+
577+
select(value: Date) {
578+
this.minDate = this.selected = value;
579+
}
580+
}

src/lib/datepicker/calendar.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
277277
constructor(_intl: MatDatepickerIntl,
278278
@Optional() private _dateAdapter: DateAdapter<D>,
279279
@Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats,
280-
changeDetectorRef: ChangeDetectorRef) {
280+
private _changeDetectorRef: ChangeDetectorRef) {
281281

282282
if (!this._dateAdapter) {
283283
throw createMissingDateImplError('DateAdapter');
@@ -288,7 +288,7 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
288288
}
289289

290290
this._intlChanges = _intl.changes.subscribe(() => {
291-
changeDetectorRef.markForCheck();
291+
_changeDetectorRef.markForCheck();
292292
this.stateChanges.next();
293293
});
294294
}
@@ -320,6 +320,9 @@ export class MatCalendar<D> implements AfterContentInit, AfterViewChecked, OnDes
320320
const view = this._getCurrentViewComponent();
321321

322322
if (view) {
323+
// We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are
324+
// passed down to the view via data bindings which won't be up-to-date when we call `_init`.
325+
this._changeDetectorRef.detectChanges();
323326
view._init();
324327
}
325328
}

0 commit comments

Comments
 (0)