diff --git a/src/lib/datepicker/calendar.spec.ts b/src/lib/datepicker/calendar.spec.ts index 35d1c4bcfeae..d5fe98145610 100644 --- a/src/lib/datepicker/calendar.spec.ts +++ b/src/lib/datepicker/calendar.spec.ts @@ -528,6 +528,57 @@ describe('MatCalendar', () => { expect(calendarInstance._activeDate).toEqual(new Date(2018, JAN, 1)); }); + + it('should re-render the month view when the minDate changes', () => { + fixture.detectChanges(); + spyOn(calendarInstance.monthView, '_init').and.callThrough(); + + testComponent.minDate = new Date(2017, NOV, 1); + fixture.detectChanges(); + + expect(calendarInstance.monthView._init).toHaveBeenCalled(); + }); + + it('should re-render the month view when the maxDate changes', () => { + fixture.detectChanges(); + spyOn(calendarInstance.monthView, '_init').and.callThrough(); + + testComponent.maxDate = new Date(2017, DEC, 1); + fixture.detectChanges(); + + expect(calendarInstance.monthView._init).toHaveBeenCalled(); + }); + + it('should re-render the year view when the minDate changes', () => { + fixture.detectChanges(); + const periodButton = + calendarElement.querySelector('.mat-calendar-period-button') as HTMLElement; + periodButton.click(); + fixture.detectChanges(); + + spyOn(calendarInstance.yearView, '_init').and.callThrough(); + + testComponent.minDate = new Date(2017, NOV, 1); + fixture.detectChanges(); + + expect(calendarInstance.yearView._init).toHaveBeenCalled(); + }); + + it('should re-render the year view when the maxDate changes', () => { + fixture.detectChanges(); + const periodButton = + calendarElement.querySelector('.mat-calendar-period-button') as HTMLElement; + periodButton.click(); + fixture.detectChanges(); + + spyOn(calendarInstance.yearView, '_init').and.callThrough(); + + testComponent.maxDate = new Date(2017, DEC, 1); + fixture.detectChanges(); + + expect(calendarInstance.yearView._init).toHaveBeenCalled(); + }); + }); describe('calendar with date filter', () => { diff --git a/src/lib/datepicker/calendar.ts b/src/lib/datepicker/calendar.ts index a046757fdbce..e73d36795b73 100644 --- a/src/lib/datepicker/calendar.ts +++ b/src/lib/datepicker/calendar.ts @@ -31,6 +31,9 @@ import { Optional, Output, ViewEncapsulation, + ViewChild, + OnChanges, + SimpleChanges, } from '@angular/core'; import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core'; import {first} from 'rxjs/operator/first'; @@ -38,6 +41,8 @@ import {Subscription} from 'rxjs/Subscription'; import {coerceDateProperty} from './coerce-date-property'; import {createMissingDateImplError} from './datepicker-errors'; import {MatDatepickerIntl} from './datepicker-intl'; +import {MatMonthView} from './month-view'; +import {MatYearView} from './year-view'; /** @@ -56,7 +61,7 @@ import {MatDatepickerIntl} from './datepicker-intl'; preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MatCalendar implements AfterContentInit, OnDestroy { +export class MatCalendar implements AfterContentInit, OnDestroy, OnChanges { private _intlChanges: Subscription; /** A date representing the period (month or year) to start the calendar in. */ @@ -95,6 +100,12 @@ export class MatCalendar implements AfterContentInit, OnDestroy { /** Emits when any date is selected. */ @Output() userSelection = new EventEmitter(); + /** Reference to the current month view component. */ + @ViewChild(MatMonthView) monthView: MatMonthView; + + /** Reference to the current year view component. */ + @ViewChild(MatYearView) yearView: MatYearView; + /** Date filter for the month and year views. */ _dateFilterForViews = (date: D) => { return !!date && @@ -166,6 +177,18 @@ export class MatCalendar implements AfterContentInit, OnDestroy { this._intlChanges.unsubscribe(); } + ngOnChanges(changes: SimpleChanges) { + const change = changes.minDate || changes.maxDate || changes.dateFilter; + + if (change && !change.firstChange) { + const view = this.monthView || this.yearView; + + if (view) { + view._init(); + } + } + } + /** Handles date selection in the month view. */ _dateSelected(date: D): void { if (!this._dateAdapter.sameDate(date, this.selected)) { diff --git a/src/lib/datepicker/month-view.ts b/src/lib/datepicker/month-view.ts index 4ac52fe8b251..f7a4c2fc4ac8 100644 --- a/src/lib/datepicker/month-view.ts +++ b/src/lib/datepicker/month-view.ts @@ -16,6 +16,7 @@ import { Optional, Output, ViewEncapsulation, + ChangeDetectorRef, } from '@angular/core'; import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core'; import {MatCalendarCell} from './calendar-body'; @@ -93,7 +94,8 @@ export class MatMonthView implements AfterContentInit { _weekdays: {long: string, narrow: string}[]; constructor(@Optional() public _dateAdapter: DateAdapter, - @Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats) { + @Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats, + private _changeDetectorRef: ChangeDetectorRef) { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -132,7 +134,7 @@ export class MatMonthView implements AfterContentInit { } /** Initializes this month view. */ - private _init() { + _init() { this._selectedDate = this._getDateInCurrentMonth(this.selected); this._todayDate = this._getDateInCurrentMonth(this._dateAdapter.today()); this._monthLabel = @@ -146,6 +148,7 @@ export class MatMonthView implements AfterContentInit { this._dateAdapter.getFirstDayOfWeek()) % DAYS_PER_WEEK; this._createWeekCells(); + this._changeDetectorRef.markForCheck(); } /** Creates MatCalendarCells for the dates in this month. */ diff --git a/src/lib/datepicker/year-view.ts b/src/lib/datepicker/year-view.ts index 11f95149e317..36c3c1babade 100644 --- a/src/lib/datepicker/year-view.ts +++ b/src/lib/datepicker/year-view.ts @@ -16,6 +16,7 @@ import { Optional, Output, ViewEncapsulation, + ChangeDetectorRef, } from '@angular/core'; import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core'; import {MatCalendarCell} from './calendar-body'; @@ -79,7 +80,8 @@ export class MatYearView implements AfterContentInit { _selectedMonth: number | null; constructor(@Optional() public _dateAdapter: DateAdapter, - @Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats) { + @Optional() @Inject(MAT_DATE_FORMATS) private _dateFormats: MatDateFormats, + private _changeDetectorRef: ChangeDetectorRef) { if (!this._dateAdapter) { throw createMissingDateImplError('DateAdapter'); } @@ -104,7 +106,7 @@ export class MatYearView implements AfterContentInit { } /** Initializes this month view. */ - private _init() { + _init() { this._selectedMonth = this._getMonthInCurrentYear(this.selected); this._todayMonth = this._getMonthInCurrentYear(this._dateAdapter.today()); this._yearLabel = this._dateAdapter.getYearName(this.activeDate); @@ -113,6 +115,7 @@ export class MatYearView implements AfterContentInit { // First row of months only contains 5 elements so we can fit the year label on the same row. this._months = [[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]].map(row => row.map( month => this._createCellForMonth(month, monthNames[month]))); + this._changeDetectorRef.markForCheck(); } /**