У меня вопрос относительно наложения и выбора даты с помощью Angular Material. Я использую событие overlayOutsideClick, чтобы закрывать наложение всякий раз, когда я щелкаю за его пределами.
Он работает нормально, но если у меня есть средство выбора даты и я выбираю дату, это рассматривается как щелчок за пределами наложения, как я могу предотвратить такое поведение?
Спасибо
Вот стекблиц, чтобы воспроизвести ситуацию: https://stackblitz.com/edit/ghjvsh-zy2lf2?file=src%2Fexample%2Fcdk-overlay-basic-example.html





Поэтому я придумал решение, которое может быть немного более сложным, чем должно быть.
Чтобы исправить закрытие наложения при нажатии на кнопку переключения выбора даты, я не использовал overlayOutsideClick, вместо этого я полагался на событие размытия для запуска логики, когда ввод даты теряет фокус.
Инкапсулируя MatFormField в div, я проверю, находится ли новый элемент в фокусе внутри этого div, прежде чем закрывать наложение, оставляя его открытым, если мы нажмем кнопку переключения выбора даты.
export class CdkOverlayBasicExample {
protected isOpen = signal<boolean>(false);
@ViewChild('container', { static: false })
datepickerContainer!: ElementRef<HTMLDivElement>;
protected onBlur($event: FocusEvent) {
const relatedTarget = $event.relatedTarget;
if (
!relatedTarget || // If the related target is null, we didn't click on any HTML element
!(relatedTarget instanceof HTMLElement) ||
!this.datepickerContainer.nativeElement.contains(relatedTarget)
) {
// If the relatedTarget is not contained inside our container div, close the overlay
this.isOpen.set(false);
}
}
}
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin] = "trigger"
[cdkConnectedOverlayOpen] = "isOpen()"
>
<div #container>
<mat-form-field>
<mat-label>Choose a date</mat-label>
<input
#input
matInput
[matDatepicker] = "picker"
(blur) = "onBlur($event)"
[cdkTrapFocusAutoCapture] = "true"
cdkTrapFocus
/>
<mat-hint>MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle
matIconSuffix
[for] = "picker"
></mat-datepicker-toggle>
<mat-datepicker
#picker
[restoreFocus] = "false"
(closed) = "input.focus()"
></mat-datepicker>
</mat-form-field>
</div>
</ng-template>
Несмотря на то, что это решение работало по назначению, у него было несколько предостережений, в частности, входные данные средства выбора даты не фокусируются по умолчанию, и закрытие средства выбора даты не будет устанавливать фокус на вводе.
Чтобы добиться лучшего результата, я добавил cdkFocusTrap из A11yModule во входные данные средства выбора даты, отключил функцию восстановления фокуса средства выбора даты и вручную установил фокус на вводе средства выбора даты, когда средство выбора даты закрыто.
import {
Component,
ElementRef,
TemplateRef,
ViewChild,
signal,
} from '@angular/core';
import { OverlayModule } from '@angular/cdk/overlay';
import {
MatDatepicker,
MatDatepickerModule,
} from '@angular/material/datepicker';
import { MatFormField, MatInputModule } from '@angular/material/input';
import { MatFormFieldModule } from '@angular/material/form-field';
import { A11yModule } from '@angular/cdk/a11y';
/**
* @title Overlay basic example
*/
@Component({
selector: 'cdk-overlay-basic-example',
template: `
<!-- This button triggers the overlay and is it's origin -->
<button
(click) = "isOpen.set(!isOpen());"
type = "button"
cdkOverlayOrigin
#trigger = "cdkOverlayOrigin"
>
{{isOpen() ? "Close" : "Open"}}
</button>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin] = "trigger"
[cdkConnectedOverlayOpen] = "isOpen()"
>
<div #container>
<mat-form-field>
<mat-label>Choose a date</mat-label>
<input
#input
matInput
[matDatepicker] = "picker"
(blur) = "onBlur($event)"
[cdkTrapFocusAutoCapture] = "true"
cdkTrapFocus
/>
<mat-hint>MM/DD/YYYY</mat-hint>
<mat-datepicker-toggle
matIconSuffix
[for] = "picker"
></mat-datepicker-toggle>
<mat-datepicker
#picker
[restoreFocus] = "false"
(closed) = "input.focus()"
></mat-datepicker>
</mat-form-field>
</div>
</ng-template>
`,
standalone: true,
imports: [
OverlayModule,
MatFormFieldModule,
MatInputModule,
MatDatepickerModule,
A11yModule,
],
})
export class CdkOverlayBasicExample {
protected isOpen = signal<boolean>(false);
@ViewChild('container', { static: false })
datepickerContainer!: ElementRef<HTMLDivElement>;
protected onBlur($event: FocusEvent) {
const relatedTarget = $event.relatedTarget;
if (
!relatedTarget ||
!(relatedTarget instanceof HTMLElement) ||
!this.datepickerContainer.nativeElement.contains(relatedTarget)
) {
this.isOpen.set(false);
}
}
}
А вот рабочий стекблиц, раздвоенный от вашего :
https://stackblitz.com/edit/ghjvsh-bmd6my?file=src%2Fexample%2Fcdk-overlay-basic-example.html
Надеюсь, это поможет !
Я хотел бы добавить, что инкапсуляция поля mat-form-field внутри элемента div не является обязательной, так как вместо этого можно просто использовать HTMLElement поля mat-form-field:
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin] = "trigger"
[cdkConnectedOverlayOpen] = "isOpen()"
>
<mat-form-field #container>
...
</mat-form-field>
</ng-template>
@ViewChild('container', { static: false })
datepickerContainer!: ElementRef<HTMLElement>;
Извините за поздний ответ, я был в отпуске :) Первые несколько дней у меня не было ответа, поэтому в итоге я использовал что-то близкое к вашему решению, используя дочерний элемент представления в mat-datepicker и проверяя открытое свойство => stackblitz.com/edit/… Ваше решение тоже работает, я поддержу его;) Еще раз спасибо
Это хорошее решение, рад, что вы нашли то, что вам подходит!
Я вижу, что вы не отреагировали на мой ответ, удалось ли вам решить проблему самостоятельно?