The month view is not working which is suspect is an angular bug, I have raised a github issue -> CALENDAR: start view month is not working hopefully it is resolved/answered.
I found this great article on setting range selection for Mat Calendar:
Using Angular Material's calendar with date ranges and range presets
We should import two providers that take care of range selection.
import {
DateRange,
MatDatepickerModule,
MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER,
DefaultMatCalendarRangeStrategy,
MatRangeDateSelectionModel,
} from '@angular/material/datepicker';
...
...
@Component({
selector: 'date-range-picker-forms-example',
templateUrl: 'date-range-picker-forms-example.html',
providers: [DefaultMatCalendarRangeStrategy, MatRangeDateSelectionModel],
After this, we can just use the strategy to determine the selection needed for the date picker.
constructor(
private readonly selectionModel: MatRangeDateSelectionModel<Date>,
private readonly selectionStrategy: DefaultMatCalendarRangeStrategy<Date>
) {}
// Event handler for when the date range selection changes.
rangeChanged(selectedDate: Date, callback: Function) {
const selection = this.selectionModel.selection,
newSelection = this.selectionStrategy.selectionFinished(
selectedDate,
selection
);
this.selectionModel.updateSelection(newSelection, this);
But we also need the form to be updated with the latest values, for this, we need to introduce a callback, which updates the form, but the selectedChange is executed inside the datepicker, so that form of the component is not visible to it, so we use .bind(this) to make sure that when the callback get's executed it has access to the component form.
<mat-calendar
[selected]="this.selectedDateRange"
[comparisonStart]="this.selectedDateRange!.start"
[comparisonEnd]="this.selectedDateRange!.end"
(selectedChange)="this.rangeChanged($event, setFormRangeControls.bind(this))"
></mat-calendar>
So the final callback sets the form values.
setFormRangeControls() {
this.range.setValue({
start: this.selectedDateRange?.start || null,
end: this.selectedDateRange?.end || null,
});
}
HTML:
<mat-form-field>
<mat-label>Enter a date range</mat-label>
<mat-date-range-input [formGroup]="range">
<input matStartDate formControlName="start" placeholder="Start date" />
<input matEndDate formControlName="end" placeholder="End date" />
</mat-date-range-input>
<mat-hint>MM/YYYY – MM/YYYY</mat-hint>
@if (range.controls.start.hasError('matStartDateInvalid')) {
<mat-error>Invalid start date</mat-error>
} @if (range.controls.end.hasError('matEndDateInvalid')) {
<mat-error>Invalid end date</mat-error>
}
</mat-form-field>
<mat-calendar
[selected]="this.selectedDateRange"
[comparisonStart]="this.selectedDateRange!.start"
[comparisonEnd]="this.selectedDateRange!.end"
(selectedChange)="this.rangeChanged($event, setFormRangeControls.bind(this))"
></mat-calendar>
TS:
import { JsonPipe } from '@angular/common';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import {
FormControl,
FormGroup,
FormsModule,
ReactiveFormsModule,
} from '@angular/forms';
import { provideNativeDateAdapter } from '@angular/material/core';
import {
DateRange,
MatDatepickerModule,
MAT_RANGE_DATE_SELECTION_MODEL_PROVIDER,
DefaultMatCalendarRangeStrategy,
MatRangeDateSelectionModel,
} from '@angular/material/datepicker';
import { MatFormFieldModule } from '@angular/material/form-field';
import * as _moment from 'moment';
// tslint:disable-next-line:no-duplicate-imports
import { default as _rollupMoment, Moment } from 'moment';
import { MatInputModule } from '@angular/material/input';
import { provideMomentDateAdapter } from '@angular/material-moment-adapter';
const moment = _rollupMoment || _moment;
// See the Moment.js docs for the meaning of these formats:
// https://momentjs.com/docs/#/displaying/format/
export const MY_FORMATS = {
parse: {
dateInput: 'MM/YYYY',
},
display: {
dateInput: 'MM/YYYY',
monthYearLabel: 'MMM YYYY',
dateA11yLabel: 'LL',
monthYearA11yLabel: 'MMMM YYYY',
},
};
/** @title Date range picker forms integration */
@Component({
selector: 'date-range-picker-forms-example',
templateUrl: 'date-range-picker-forms-example.html',
providers: [DefaultMatCalendarRangeStrategy, MatRangeDateSelectionModel],
imports: [
MatFormFieldModule,
MatDatepickerModule,
FormsModule,
ReactiveFormsModule,
JsonPipe,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DateRangePickerFormsExample {
readonly range = new FormGroup({
start: new FormControl<Date | null>(null),
end: new FormControl<Date | null>(null),
});
selectedDateRange: DateRange<Date | null> | undefined = new DateRange(
null,
null
);
constructor(
private readonly selectionModel: MatRangeDateSelectionModel<Date>,
private readonly selectionStrategy: DefaultMatCalendarRangeStrategy<Date>
) {}
// Event handler for when the date range selection changes.
rangeChanged(selectedDate: Date, callback: Function) {
const selection = this.selectionModel.selection,
newSelection = this.selectionStrategy.selectionFinished(
selectedDate,
selection
);
this.selectionModel.updateSelection(newSelection, this);
// sync the selection the form controls
this.selectedDateRange = new DateRange<Date>(
newSelection.start,
newSelection.end
);
if (callback) {
callback();
}
}
setFormRangeControls() {
this.range.setValue({
start: this.selectedDateRange?.start || null,
end: this.selectedDateRange?.end || null,
});
}
}