Угловой материал — Overlay X Datepicker

У меня вопрос относительно наложения и выбора даты с помощью Angular Material. Я использую событие overlayOutsideClick, чтобы закрывать наложение всякий раз, когда я щелкаю за его пределами.

Он работает нормально, но если у меня есть средство выбора даты и я выбираю дату, это рассматривается как щелчок за пределами наложения, как я могу предотвратить такое поведение?

Спасибо

Вот стекблиц, чтобы воспроизвести ситуацию: https://stackblitz.com/edit/ghjvsh-zy2lf2?file=src%2Fexample%2Fcdk-overlay-basic-example.html

Я вижу, что вы не отреагировали на мой ответ, удалось ли вам решить проблему самостоятельно?

Tibère B. 07.05.2024 15:48
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
2
1
118
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поэтому я придумал решение, которое может быть немного более сложным, чем должно быть.

Чтобы исправить закрытие наложения при нажатии на кнопку переключения выбора даты, я не использовал 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/… Ваше решение тоже работает, я поддержу его;) Еще раз спасибо

Ggros 15.05.2024 12:07

Это хорошее решение, рад, что вы нашли то, что вам подходит!

Tibère B. 15.05.2024 13:28

Другие вопросы по теме