The way I prevented the overlay to close was to change MatCalendar._userSelected method.
If you look into the template of MatDatePickerContent, you would notice the call for when _userSelection is emitting. The call is to close the overlay: (_userSelection)="datepicker.close()"
.
Now, _userSelection is emitting in MatCalendar._userSelected.
datepicker.js, line 1687, "@angular/material": "8.2.3":
_userSelected() {
this._userSelection.emit();
}
Because I did not wanted this behaviour for all the instances of DatePicker in the application, in my component I did as follow:
@ViewChild('picker', { static: true })
private picker: MatDatepicker<Date>;
private preventCloseOnSelection = true;
private readonly initCalendarUserSelected: () => void;
public model: Array<Date>;
constructor(private readonly cdr: ChangeDetectorRef) {
this.initCalendarUserSelected = MatCalendar.prototype._userSelected;
}
public ngAfterViewInit() {
this.picker.calendarHeaderComponent = CalendarTimeHeaderComponent;
this.picker.openedStream
.pipe(takeUntil(this.isUnsubscribing))
.subscribe(() => {
if (this.preventCloseOnSelection) {
MatCalendar.prototype._userSelected = () => {};
} else {
MatCalendar.prototype._userSelected = this.initCalendarUserSelected;
}
this.cdr.detectChanges();
});
}
public ngOnDestroy() {
MatCalendar.prototype._userSelected = this.initCalendarUserSelected;
}
If you want this behaviour for all the instances of date picker, you can override the MatCalendar._userSelected method somewhere in AppComponent and not bother to restore it.
One other way would be to do this.picker.close = () => { };
after the date is changed, but you have to restore it always to be able to close the overlay.
You can get the selected date with the dateChange event of the matDatepickerInput and create an array with dates for multiple values and for having them selected in the view, you can use the dateClass input of the matDatepicker.
View:
<input [(ngModel)]="model" matInput [matDatepicker]="picker" placeholder="Choose a date" (dateChange)="dateChanged($event)" />
<mat-datepicker-toggle matPrefix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker [dateClass]="dateClass"></mat-datepicker>
Methods:
public dateClass = (date: Date) => {
if (this.model.map((m) => +m).indexOf(+date) !== -1) {
return [ 'selected' ];
}
return [ ];
}
public dateChanged(event: MatDatepickerInputEvent<Date>) {
if (event.value) {
const date = event.value;
const index = this.model.map((m) => +m).indexOf(+date);
if (index === -1) {
this.model.push(date);
} else {
this.model.splice(index, 1)
}
}
}