Skip to content

Commit

Permalink
fix(calendar): not reacting to min/max boundary changes
Browse files Browse the repository at this point in the history
Fixes the calendar not re-rendering when the `minDate`, `maxDate` or `dateFilter` change. The issue was due to the fact that the `minDate`, `maxDate` and `dateFilter` weren't being passed down to the views via `@Input`.

Fixes #7202.
  • Loading branch information
crisbeto committed Sep 21, 2017
1 parent dbae360 commit be2e1f5
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 5 deletions.
51 changes: 51 additions & 0 deletions src/lib/datepicker/calendar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,57 @@ describe('MdCalendar', () => {

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', () => {
Expand Down
25 changes: 24 additions & 1 deletion src/lib/datepicker/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import {
Optional,
Output,
ViewEncapsulation,
ViewChild,
OnChanges,
SimpleChanges,
} from '@angular/core';
import {
DateAdapter,
Expand All @@ -43,6 +46,8 @@ import {Subscription} from 'rxjs/Subscription';
import {coerceDateProperty} from './coerce-date-property';
import {createMissingDateImplError} from './datepicker-errors';
import {MdDatepickerIntl} from './datepicker-intl';
import {MdMonthView} from './month-view';
import {MdYearView} from './year-view';


/**
Expand All @@ -62,7 +67,7 @@ import {MdDatepickerIntl} from './datepicker-intl';
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [{provide: MATERIAL_COMPATIBILITY_MODE, useValue: true}],
})
export class MdCalendar<D> implements AfterContentInit, OnDestroy {
export class MdCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
private _intlChanges: Subscription;

/** A date representing the period (month or year) to start the calendar in. */
Expand Down Expand Up @@ -101,6 +106,12 @@ export class MdCalendar<D> implements AfterContentInit, OnDestroy {
/** Emits when any date is selected. */
@Output() userSelection = new EventEmitter<void>();

/** Reference to the current month view component. */
@ViewChild(MdMonthView) monthView: MdMonthView<D>;

/** Reference to the current year view component. */
@ViewChild(MdYearView) yearView: MdYearView<D>;

/** Date filter for the month and year views. */
_dateFilterForViews = (date: D) => {
return !!date &&
Expand Down Expand Up @@ -172,6 +183,18 @@ export class MdCalendar<D> 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)) {
Expand Down
7 changes: 5 additions & 2 deletions src/lib/datepicker/month-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Optional,
Output,
ViewEncapsulation,
ChangeDetectorRef,
} from '@angular/core';
import {
DateAdapter, MATERIAL_COMPATIBILITY_MODE, MD_DATE_FORMATS,
Expand Down Expand Up @@ -97,7 +98,8 @@ export class MdMonthView<D> implements AfterContentInit {
_weekdays: {long: string, narrow: string}[];

constructor(@Optional() public _dateAdapter: DateAdapter<D>,
@Optional() @Inject(MD_DATE_FORMATS) private _dateFormats: MdDateFormats) {
@Optional() @Inject(MD_DATE_FORMATS) private _dateFormats: MdDateFormats,
private _changeDetectorRef: ChangeDetectorRef) {
if (!this._dateAdapter) {
throw createMissingDateImplError('DateAdapter');
}
Expand Down Expand Up @@ -136,7 +138,7 @@ export class MdMonthView<D> implements AfterContentInit {
}

/** Initializes this month view. */
private _init() {
_init() {
this._selectedDate = this._getDateInCurrentMonth(this.selected);
this._todayDate = this._getDateInCurrentMonth(this._dateAdapter.today());
this._monthLabel =
Expand All @@ -150,6 +152,7 @@ export class MdMonthView<D> implements AfterContentInit {
this._dateAdapter.getFirstDayOfWeek()) % DAYS_PER_WEEK;

this._createWeekCells();
this._changeDetectorRef.markForCheck();
}

/** Creates MdCalendarCells for the dates in this month. */
Expand Down
7 changes: 5 additions & 2 deletions src/lib/datepicker/year-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Optional,
Output,
ViewEncapsulation,
ChangeDetectorRef,
} from '@angular/core';
import {DateAdapter, MD_DATE_FORMATS, MdDateFormats} from '@angular/material/core';
import {MdCalendarCell} from './calendar-body';
Expand Down Expand Up @@ -79,7 +80,8 @@ export class MdYearView<D> implements AfterContentInit {
_selectedMonth: number | null;

constructor(@Optional() public _dateAdapter: DateAdapter<D>,
@Optional() @Inject(MD_DATE_FORMATS) private _dateFormats: MdDateFormats) {
@Optional() @Inject(MD_DATE_FORMATS) private _dateFormats: MdDateFormats,
private _changeDetectorRef: ChangeDetectorRef) {
if (!this._dateAdapter) {
throw createMissingDateImplError('DateAdapter');
}
Expand All @@ -104,7 +106,7 @@ export class MdYearView<D> 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);
Expand All @@ -113,6 +115,7 @@ export class MdYearView<D> 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();
}

/**
Expand Down

0 comments on commit be2e1f5

Please sign in to comment.