Предотвращение закрытия mat-datepicker при выборе даты
У меня есть требование, чтобы пользователь мог выбрать несколько дат в datepicker, но после каждого выбора даты mat-datepicker закрывается, я хочу предотвратить закрытие mat-datepicker при выборе даты и закрыть datepicker, когда мы щелкаем за пределами datepicker или на значке календаря.
Я попытался closed
событие на mat-datepicker, но выбор даты мигает при каждом выборе даты, плюс он не закрывается, когда мы щелкаем за пределами datepicker.
Вот что я пробовал:
html-код:
<input formControlName="fromDate" matInput [matDatepicker]="fromDatePicker" placeholder="From Date" [min]="minDate" readonly>
<mat-datepicker-toggle matSuffix [for]="fromDatePicker"></mat-datepicker-toggle>
<mat-datepicker #fromDatePicker (closed)="_openCalendar(fromDatePicker)"></mat-datepicker>
машинописный код:
_openCalendar(picker: MatDatepicker<Date>) {
picker.open();
}
3 ответа
Я предотвратил закрытие оверлея путем изменения метода MatCalendar._userSelected.
Если вы посмотрите на шаблон MatDatePickerContent, вы заметите вызов, когда испускается _userSelection. Призыв закрыть оверлей:(_userSelection)="datepicker.close()"
. Теперь _userSelection излучает в MatCalendar._userSelected.
datepicker.js, строка 1687, "@angular/material": "8.2.3":
_userSelected() {
this._userSelection.emit();
}
Поскольку мне не нужно было такое поведение для всех экземпляров DatePicker в приложении, в моем компоненте я сделал следующее:
@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;
}
Если вам нужно такое поведение для всех экземпляров средства выбора даты, вы можете переопределить метод MatCalendar._userSelected где-нибудь в AppComponent и не беспокоиться о его восстановлении.
Еще один способ - сделать this.picker.close = () => { };
после изменения даты, но вы всегда должны восстанавливать ее, чтобы можно было закрыть оверлей.
Вы можете получить выбранную дату с помощью события dateChange в matDatepickerInput и создать массив с датами для нескольких значений, а для их выбора в представлении вы можете использовать вход dateClass для matDatepicker.
Посмотреть:
<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>
Методы:
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)
}
}
}
в случае выбора диапазона дат я решил эту проблему, как показано ниже:
export class MatDatetimeComponent {
@ViewChild(MatDateRangePicker) containerElRef;
constructor(
private resolver: ComponentFactoryResolver,
private cdr: ChangeDetectorRef,
private cdkConnectedOverlay: OverlayOutsideClickDispatcher
) {}
// store prototype close object
private selfClose: () => void;
ngAfterViewInit() {
this.selfClose = this.containerElRef.close;
}
public onOpen() {
// rewrite autoclose after date is chosen
this.containerElRef.close = () => {};
// close calendar manually on outside click
this.cdkConnectedOverlay._attachedOverlays[0]._outsidePointerEvents.subscribe(() => {
// restore saved close method
this.containerElRef.close = this.selfClose;
this.containerElRef.close();
});
}
}
Поскольку другие Решения у меня не работали, я сделал следующее:
Машинописный код:
@ViewChild(MatDatepicker) datePicker: MatDatepicker<Date>;
constructor(
private changeDetectorRef: ChangeDetectorRef,
private cdkConnectedOverlay: OverlayOutsideClickDispatcher) { }
// store prototype close object
private selfClose: () => void;
opened() {
if (isNullOrUndefined(this.selfClose)) {
this.selfClose = this.datePicker.close;
}
// rewrite autoclose after date is chosen
this.datePicker.close = () => {};
// close calendar manually on outside click
this.cdkConnectedOverlay._attachedOverlays[0]._outsidePointerEvents.subscribe(() => {
// restore saved close method
this.datePicker.close = this.selfClose;
this.selfClose = undefined;
this.datePicker.close();
});
}
Код схемы:
<mat-datepicker #picker
(opened)="opened()"
></mat-datepicker>
Может быть, это кому-то поможет.