mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(calendar): not reacting to min/max boundary changes (#7234)
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.
This commit is contained in:
committed by
Andrew Seguin
parent
6be046261a
commit
eb012cc9b7
@@ -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', () => {
|
||||
|
||||
@@ -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<D> implements AfterContentInit, OnDestroy {
|
||||
export class MatCalendar<D> 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<D> implements AfterContentInit, OnDestroy {
|
||||
/** Emits when any date is selected. */
|
||||
@Output() userSelection = new EventEmitter<void>();
|
||||
|
||||
/** Reference to the current month view component. */
|
||||
@ViewChild(MatMonthView) monthView: MatMonthView<D>;
|
||||
|
||||
/** Reference to the current year view component. */
|
||||
@ViewChild(MatYearView) yearView: MatYearView<D>;
|
||||
|
||||
/** Date filter for the month and year views. */
|
||||
_dateFilterForViews = (date: D) => {
|
||||
return !!date &&
|
||||
@@ -166,6 +177,18 @@ export class MatCalendar<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)) {
|
||||
|
||||
@@ -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<D> implements AfterContentInit {
|
||||
_weekdays: {long: string, narrow: string}[];
|
||||
|
||||
constructor(@Optional() public _dateAdapter: DateAdapter<D>,
|
||||
@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<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 =
|
||||
@@ -146,6 +148,7 @@ export class MatMonthView<D> implements AfterContentInit {
|
||||
this._dateAdapter.getFirstDayOfWeek()) % DAYS_PER_WEEK;
|
||||
|
||||
this._createWeekCells();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
/** Creates MatCalendarCells for the dates in this month. */
|
||||
|
||||
@@ -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<D> implements AfterContentInit {
|
||||
_selectedMonth: number | null;
|
||||
|
||||
constructor(@Optional() public _dateAdapter: DateAdapter<D>,
|
||||
@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<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);
|
||||
@@ -113,6 +115,7 @@ export class MatYearView<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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user