From b1d4fe15b7d1b386dfba470e91c1633b741f3ac6 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 12 Jul 2018 16:53:09 +0200 Subject: [PATCH] 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. --- src/lib/datepicker/calendar.spec.ts | 59 ++++++++++++++++++++++++++++- src/lib/datepicker/calendar.ts | 7 +++- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/lib/datepicker/calendar.spec.ts b/src/lib/datepicker/calendar.spec.ts index a5f389c473fc..644271d203c3 100644 --- a/src/lib/datepicker/calendar.spec.ts +++ b/src/lib/datepicker/calendar.spec.ts @@ -10,7 +10,7 @@ import { } from '@angular/cdk/testing'; import {Component, NgZone} from '@angular/core'; import {ComponentFixture, TestBed, async, inject} from '@angular/core/testing'; -import {DEC, FEB, JAN, MatNativeDateModule, NOV} from '@angular/material/core'; +import {DEC, FEB, JAN, MatNativeDateModule, NOV, JUL} from '@angular/material/core'; import {By} from '@angular/platform-browser'; import {Direction, Directionality} from '@angular/cdk/bidi'; import {MatCalendar} from './calendar'; @@ -32,6 +32,7 @@ describe('MatCalendar', () => { StandardCalendar, CalendarWithMinMax, CalendarWithDateFilter, + CalendarWithSelectableMinDate, ], providers: [ MatDatepickerIntl, @@ -399,6 +400,37 @@ describe('MatCalendar', () => { expect(calendarInstance.multiYearView._init).toHaveBeenCalled(); }); + + it('should update the minDate in the child view if it changed after an interaction', () => { + fixture.destroy(); + + const dynamicFixture = TestBed.createComponent(CalendarWithSelectableMinDate); + dynamicFixture.detectChanges(); + + const calendarDebugElement = dynamicFixture.debugElement.query(By.directive(MatCalendar)); + const disabledClass = 'mat-calendar-body-disabled'; + calendarElement = calendarDebugElement.nativeElement; + calendarInstance = calendarDebugElement.componentInstance; + + let cells = Array.from(calendarElement.querySelectorAll('.mat-calendar-body-cell')); + + expect(cells.slice(0, 9).every(c => c.classList.contains(disabledClass))) + .toBe(true, 'Expected dates up to the 10th to be disabled.'); + + expect(cells.slice(9).every(c => c.classList.contains(disabledClass))) + .toBe(false, 'Expected dates after the 10th to be enabled.'); + + (cells[14] as HTMLElement).click(); + dynamicFixture.detectChanges(); + cells = Array.from(calendarElement.querySelectorAll('.mat-calendar-body-cell')); + + expect(cells.slice(0, 14).every(c => c.classList.contains(disabledClass))) + .toBe(true, 'Expected dates up to the 14th to be disabled.'); + + expect(cells.slice(14).every(c => c.classList.contains(disabledClass))) + .toBe(false, 'Expected dates after the 14th to be enabled.'); + }); + }); describe('calendar with date filter', () => { @@ -521,3 +553,28 @@ class CalendarWithDateFilter { return !(date.getDate() % 2) && date.getMonth() !== NOV; } } + + +@Component({ + template: ` + + + ` +}) +class CalendarWithSelectableMinDate { + startAt = new Date(2018, JUL, 0); + selected: Date; + minDate: Date; + + constructor() { + this.select(new Date(2018, JUL, 10)); + } + + select(value: Date) { + this.minDate = this.selected = value; + } +} diff --git a/src/lib/datepicker/calendar.ts b/src/lib/datepicker/calendar.ts index a1215d3ec4d0..4c3411e3c501 100644 --- a/src/lib/datepicker/calendar.ts +++ b/src/lib/datepicker/calendar.ts @@ -277,7 +277,7 @@ export class MatCalendar implements AfterContentInit, AfterViewChecked, OnDes constructor(_intl: MatDatepickerIntl, @Optional() private _dateAdapter: DateAdapter, @Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats, - changeDetectorRef: ChangeDetectorRef) { + private _changeDetectorRef: ChangeDetectorRef) { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); @@ -288,7 +288,7 @@ export class MatCalendar implements AfterContentInit, AfterViewChecked, OnDes } this._intlChanges = _intl.changes.subscribe(() => { - changeDetectorRef.markForCheck(); + _changeDetectorRef.markForCheck(); this.stateChanges.next(); }); } @@ -320,6 +320,9 @@ export class MatCalendar implements AfterContentInit, AfterViewChecked, OnDes const view = this._getCurrentViewComponent(); if (view) { + // We need to `detectChanges` manually here, because the `minDate`, `maxDate` etc. are + // passed down to the view via data bindings which won't be up-to-date when we call `_init`. + this._changeDetectorRef.detectChanges(); view._init(); } }