diff --git a/src/lib/datepicker/calendar.html b/src/lib/datepicker/calendar.html
index 7b06f3296eab..5fab7ec8735e 100644
--- a/src/lib/datepicker/calendar.html
+++ b/src/lib/datepicker/calendar.html
@@ -26,7 +26,9 @@
*ngSwitchCase="'month'"
[activeDate]="_activeDate"
[selected]="selected"
- [dateFilter]="_dateFilterForViews"
+ [dateFilter]="dateFilter"
+ [maxDate]="maxDate"
+ [minDate]="minDate"
(selectedChange)="_dateSelected($event)"
(_userSelection)="_userSelected()">
@@ -35,7 +37,9 @@
*ngSwitchCase="'year'"
[activeDate]="_activeDate"
[selected]="selected"
- [dateFilter]="_dateFilterForViews"
+ [dateFilter]="dateFilter"
+ [maxDate]="maxDate"
+ [minDate]="minDate"
(monthSelected)="_monthSelectedInYearView($event)"
(selectedChange)="_goToDateInView($event, 'month')">
@@ -44,7 +48,9 @@
*ngSwitchCase="'multi-year'"
[activeDate]="_activeDate"
[selected]="selected"
- [dateFilter]="_dateFilterForViews"
+ [dateFilter]="dateFilter"
+ [maxDate]="maxDate"
+ [minDate]="minDate"
(yearSelected)="_yearSelectedInMultiYearView($event)"
(selectedChange)="_goToDateInView($event, 'year')">
diff --git a/src/lib/datepicker/calendar.ts b/src/lib/datepicker/calendar.ts
index 1cbc49ebce93..644496adc52f 100644
--- a/src/lib/datepicker/calendar.ts
+++ b/src/lib/datepicker/calendar.ts
@@ -131,14 +131,6 @@ export class MatCalendar implements AfterContentInit, OnDestroy, OnChanges {
/** Reference to the current multi-year view component. */
@ViewChild(MatMultiYearView) multiYearView: MatMultiYearView;
- /** Date filter for the month, year, and multi-year views. */
- _dateFilterForViews = (date: D) => {
- return !!date &&
- (!this.dateFilter || this.dateFilter(date)) &&
- (!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) &&
- (!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0);
- }
-
/**
* The current active date. This determines which time period is shown and which date is
* highlighted when using keyboard navigation.
@@ -368,7 +360,7 @@ export class MatCalendar implements AfterContentInit, OnDestroy, OnChanges {
this._dateAdapter.addCalendarMonths(this._activeDate, 1);
break;
case ENTER:
- if (this._dateFilterForViews(this._activeDate)) {
+ if (!this.dateFilter || this.dateFilter(this._activeDate)) {
this._dateSelected(this._activeDate);
this._userSelected();
// Prevent unexpected default actions such as form submission.
diff --git a/src/lib/datepicker/month-view.ts b/src/lib/datepicker/month-view.ts
index eb5ef809b9df..933608feef60 100644
--- a/src/lib/datepicker/month-view.ts
+++ b/src/lib/datepicker/month-view.ts
@@ -64,6 +64,22 @@ export class MatMonthView implements AfterContentInit {
}
private _selected: D | null;
+ /** The minimum selectable date. */
+ @Input()
+ get minDate(): D | null { return this._minDate; }
+ set minDate(value: D | null) {
+ this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
+ }
+ private _minDate: D | null;
+
+ /** The maximum selectable date. */
+ @Input()
+ get maxDate(): D | null { return this._maxDate; }
+ set maxDate(value: D | null) {
+ this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
+ }
+ private _maxDate: D | null;
+
/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@@ -154,25 +170,32 @@ export class MatMonthView implements AfterContentInit {
/** Creates MatCalendarCells for the dates in this month. */
private _createWeekCells() {
- let daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate);
- let dateNames = this._dateAdapter.getDateNames();
+ const daysInMonth = this._dateAdapter.getNumDaysInMonth(this.activeDate);
+ const dateNames = this._dateAdapter.getDateNames();
this._weeks = [[]];
for (let i = 0, cell = this._firstWeekOffset; i < daysInMonth; i++, cell++) {
if (cell == DAYS_PER_WEEK) {
this._weeks.push([]);
cell = 0;
}
- let date = this._dateAdapter.createDate(
- this._dateAdapter.getYear(this.activeDate),
- this._dateAdapter.getMonth(this.activeDate), i + 1);
- let enabled = !this.dateFilter ||
- this.dateFilter(date);
- let ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel);
+ const date = this._dateAdapter.createDate(
+ this._dateAdapter.getYear(this.activeDate),
+ this._dateAdapter.getMonth(this.activeDate), i + 1);
+ const enabled = this._shouldEnableDate(date);
+ const ariaLabel = this._dateAdapter.format(date, this._dateFormats.display.dateA11yLabel);
this._weeks[this._weeks.length - 1]
.push(new MatCalendarCell(i + 1, dateNames[i], ariaLabel, enabled));
}
}
+ /** Date filter for the month */
+ private _shouldEnableDate(date: D): boolean {
+ return !!date &&
+ (!this.dateFilter || this.dateFilter(date)) &&
+ (!this.minDate || this._dateAdapter.compareDate(date, this.minDate) >= 0) &&
+ (!this.maxDate || this._dateAdapter.compareDate(date, this.maxDate) <= 0);
+ }
+
/**
* Gets the date in this month that the given Date falls on.
* Returns null if the given Date is in another month.
diff --git a/src/lib/datepicker/multi-year-view.ts b/src/lib/datepicker/multi-year-view.ts
index 65ebf88610b9..7935aa481156 100644
--- a/src/lib/datepicker/multi-year-view.ts
+++ b/src/lib/datepicker/multi-year-view.ts
@@ -64,6 +64,22 @@ export class MatMultiYearView implements AfterContentInit {
}
private _selected: D | null;
+ /** The minimum selectable date. */
+ @Input()
+ get minDate(): D | null { return this._minDate; }
+ set minDate(value: D | null) {
+ this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
+ }
+ private _minDate: D | null;
+
+ /** The maximum selectable date. */
+ @Input()
+ get maxDate(): D | null { return this._maxDate; }
+ set maxDate(value: D | null) {
+ this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
+ }
+ private _maxDate: D | null;
+
/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@@ -128,11 +144,19 @@ export class MatMultiYearView implements AfterContentInit {
/** Creates an MatCalendarCell for the given year. */
private _createCellForYear(year: number) {
let yearName = this._dateAdapter.getYearName(this._dateAdapter.createDate(year, 0, 1));
- return new MatCalendarCell(year, yearName, yearName, this._isYearEnabled(year));
+ return new MatCalendarCell(year, yearName, yearName, this._shouldEnableYear(year));
}
/** Whether the given year is enabled. */
- private _isYearEnabled(year: number) {
+ private _shouldEnableYear(year: number) {
+ // disable if the year is greater than maxDate lower than minDate
+ if (year === undefined || year === null ||
+ (this.maxDate && year > this._dateAdapter.getYear(this.maxDate)) ||
+ (this.minDate && year < this._dateAdapter.getYear(this.minDate))) {
+ return false;
+ }
+
+ // enable if it reaches here and there's no filter defined
if (!this.dateFilter) {
return true;
}
diff --git a/src/lib/datepicker/year-view.ts b/src/lib/datepicker/year-view.ts
index 4b948f4ac789..cfa082275d48 100644
--- a/src/lib/datepicker/year-view.ts
+++ b/src/lib/datepicker/year-view.ts
@@ -59,6 +59,22 @@ export class MatYearView implements AfterContentInit {
}
private _selected: D | null;
+ /** The minimum selectable date. */
+ @Input()
+ get minDate(): D | null { return this._minDate; }
+ set minDate(value: D | null) {
+ this._minDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
+ }
+ private _minDate: D | null;
+
+ /** The maximum selectable date. */
+ @Input()
+ get maxDate(): D | null { return this._maxDate; }
+ set maxDate(value: D | null) {
+ this._maxDate = this._getValidDateOrNull(this._dateAdapter.deserialize(value));
+ }
+ private _maxDate: D | null;
+
/** A function used to filter which dates are selectable. */
@Input() dateFilter: (date: D) => boolean;
@@ -142,17 +158,25 @@ export class MatYearView implements AfterContentInit {
this._dateAdapter.createDate(this._dateAdapter.getYear(this.activeDate), month, 1),
this._dateFormats.display.monthYearA11yLabel);
return new MatCalendarCell(
- month, monthName.toLocaleUpperCase(), ariaLabel, this._isMonthEnabled(month));
+ month, monthName.toLocaleUpperCase(), ariaLabel, this._shouldEnableMonth(month));
}
/** Whether the given month is enabled. */
- private _isMonthEnabled(month: number) {
+ private _shouldEnableMonth(month: number) {
+
+ const activeYear = this._dateAdapter.getYear(this.activeDate);
+
+ if (month === undefined || month === null ||
+ this._isYearAndMonthAfterMaxDate(activeYear, month) ||
+ this._isYearAndMonthBeforeMinDate(activeYear, month)) {
+ return false;
+ }
+
if (!this.dateFilter) {
return true;
}
- let firstOfMonth = this._dateAdapter.createDate(
- this._dateAdapter.getYear(this.activeDate), month, 1);
+ const firstOfMonth = this._dateAdapter.createDate(activeYear, month, 1);
// If any date in the month is enabled count the month as enabled.
for (let date = firstOfMonth; this._dateAdapter.getMonth(date) == month;
@@ -165,6 +189,36 @@ export class MatYearView implements AfterContentInit {
return false;
}
+ /**
+ * Tests whether the combination month/year is after this.maxDate, considering
+ * just the month and year of this.maxDate
+ */
+ private _isYearAndMonthAfterMaxDate(year: number, month: number) {
+ if (this.maxDate) {
+ const maxYear = this._dateAdapter.getYear(this.maxDate);
+ const maxMonth = this._dateAdapter.getMonth(this.maxDate);
+
+ return year > maxYear || (year === maxYear && month > maxMonth);
+ }
+
+ return false;
+ }
+
+ /**
+ * Tests whether the combination month/year is before this.minDate, considering
+ * just the month and year of this.minDate
+ */
+ private _isYearAndMonthBeforeMinDate(year: number, month: number) {
+ if (this.minDate) {
+ const minYear = this._dateAdapter.getYear(this.minDate);
+ const minMonth = this._dateAdapter.getMonth(this.minDate);
+
+ return year < minYear || (year === minYear && month < minMonth);
+ }
+
+ return false;
+ }
+
/**
* @param obj The object to check.
* @returns The given object if it is both a date instance and valid, otherwise null.