[Angular] Построение компонентов с выпадающими блоками с помощью Angular и Material CDK

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
18-Июн-2021 17:31

Каждое приложение использует компоненты с выпадающими блоками. Такие панели используются в выпадающем списке, Autocomplete, Tooltip и т.д. В Material CDK есть инструмент Overlay для создания такого функционала.
Зачем это нужно? Z-index и вперед!С точки зрения простого html и css, мы можем использовать Z-index. Например, для dropdownmenu есть следующий рецепт. Но как писал Александр Инкин в своей статье, мы получим войну миров z-index. У Taiga UI есть свой способ решить эту проблему. Рассмотрим альтернативный инструмент от команды Material - CDK Overlay.Кто применяетМожет показаться, что Material CDK обязывает использовать нас библиотеку компонентов Angular Material, но это не так. CDK и его базовые стили можно подключить отдельно. Благодаря этому команда создателей CDK дала возможность использовать их наработки в своих проектах. Более того, популярные и крупные проекты тоже активно используют этот CDK. Библиотеки компонентов, которые используют Material CDK:
  • Material
  • Ant Design (ng-zorro)
Какие компоненты можно сделать на базе CDK overlay:
  • Tooltip 
  • Dialog
  • Select
  • Autocomplete 
  • DropDawnMenu 
  • TreeSelect
  • Calendar
Почему бы не попробовать использовать его в своей работе, ведь он облегчает решение задач. Ниже мы рассмотрим примеры, как его использовать, и создадим компонент.Начало работы, подключим CDKУстановить пакет
npm i @angular/cdk
Можно работать без основных материал компонентов, для этого нужно подключить стиль CDKПо ходу создания компонентов будем описывать Api CDK Overlay. Итак, приступим к созданию Tooltip!Требования к TooltipПеред началом реализации подумаем, как мы его хотим использовать и из чего он будет состоять. Для этого зададим к нему небольшие требования.
  • Не использует Z-index!
  • Может позиционироваться как над, так и под элементом, к которому применяется подсказка и зависит от пространства сверху/снизу. Если нет места сверху на экране, он должен отображаться снизу.
  • Работает в 2-х режимах. Первый скрывает подсказку, если исчезает фокус с элемента (так работает Material). Второй не исчезает (так работает ng-zorro), чтобы можно было скопировать текст или совершить какие-либо действия.
  • Может отображать простой текст или шаблон.
  • Должен иметь легкий API для использования.
Архитектура Tooltip
  • Директива - используется в компоненте, к которому применяется Tooltip
  • Компонент - это отображение самого Tooltip.
Принцип работы
  • Директива добавляет функциональность к компоненту. При событии Mouseenter будет создаваться компонент. В работе будут использованы следующие API Angular: ComponentFactoryResolver и ViewContainerRef - с их помощью будет создаваться компонент TooltipComponent.ElementRef - для получения ссылки на host элемент, она потребуется для OverlayCdk.Renderer2 - с его помощью него можно подписаться на события (Mouseenter)
  • После создания инстанса компонента ему устанавливаются параметры:title - простой текст;view - ссылка на шаблон;origin - ссылка на host компонент.
  • Теперь самое интересное, работа с СDK Overlay:cdkConnectedOverlay - директива для декларативного создания всплывающих элементов;cdkConnectedOverlayOrigin - сюда передается наш host, элемент относительно которого будет создаваться всплывающая область;cdkConnectedOverlayPositions - список возможного позиционирования всплывающей области. В ней можно настроить всплывающую область сверху, снизу, слева, справа и задать дополнительный класс css. В примере настройка для всплытия снизу и сверху.
Код директивы
@Directive({
  selector: '[ui-tooltip]',
  exportAs: 'uiTooltip'
})
export class TooltipDirective implements OnInit, OnDestroy, AfterViewInit {
  @Input()
  content: string;
  @Input()
  view: TemplateRef<any>;
  component: TooltipComponent;
  componentFactory: ComponentFactory<
    TooltipComponent
  > = this.resolver.resolveComponentFactory(TooltipComponent);
  protected readonly disposables: Array<() => void> = [];
  constructor(
    private elementRef: ElementRef,
    private hostView: ViewContainerRef,
    private renderer: Renderer2,
    private resolver: ComponentFactoryResolver
  ) {}
  ngOnInit(): void {}
  ngAfterViewInit(): void {
    this.registerTriggers();
  }
  createComponent(): void {
    const componentRef = this.hostView.createComponent(this.componentFactory);
    this.component = componentRef.instance as TooltipComponent;
    this.component.setTitle(this.content);
    this.component.setView(this.view);
    this.component.setOverlayOrigin({ elementRef: this.elementRef });
    this.component.mouseleave.subscribe(x => {
      this.component.hide();
    });
  }
  registerTriggers(): void {
    const el = this.elementRef.nativeElement;
    const listnerMouseenter = this.renderer.listen(el, 'mouseenter', () => {
      this.createComponent();
    });
    this.disposables.push(listnerMouseenter);
    const listnerMouseleave = this.renderer.listen(el, 'mouseleave', () => {
      console.log(el);
      setTimeout(x => {
        if (!this.component.isActive) {
          this.component.hide();
        }
      }, 50);
    });
    this.disposables.push(listnerMouseleave);
  }
  ngOnDestroy(): void {
    this.disposables.forEach(dispose => dispose());
  }
}
Код Tooltip
@Component({
  selector: 'uikit-tooltip',
  exportAs: 'uikitTooltip',
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template: `
    <ng-template
      #overlay="cdkConnectedOverlay"
      cdkConnectedOverlay
      [cdkConnectedOverlayOrigin]="origin"
      [cdkConnectedOverlayOpen]="visible"
      [cdkConnectedOverlayPositions]="positions"
      [cdkConnectedOverlayPush]="true"
      ,
      (detach)="hide()"
      (positionChange)="positionChange($event)"
    >
      <div #tooltip class="tooltip-container">
        <div class="tooltip-body">
          <div *ngIf="title">{{ title }}</div>
          <div *ngIf="view">
            <ng-template [ngTemplateOutlet]="$any(view)"></ng-template>
          </div>
        </div>
      </div>
    </ng-template>
  `,
  styleUrls: ['./tooltip.component.scss']
})
export class TooltipComponent implements OnInit, AfterViewInit {
  title: string;
  view: TemplateRef<any>;
  isActive = false;
  @ViewChild('overlay', { static: false }) overlay!: CdkConnectedOverlay;
  @ViewChild('tooltip', { static: false }) tooltip!: ElementRef;
  @Output() mouseleave = new EventEmitter();
  origin!: CdkOverlayOrigin;
  positions = [
    new ConnectionPositionPair(
      { originX: 'center', originY: 'top' },
      { overlayX: 'center', overlayY: 'bottom' },
      0,
      0,
      'tooltip-body-top'
    ),
    new ConnectionPositionPair(
      { originX: 'center', originY: 'bottom' },
      { overlayX: 'center', overlayY: 'top' },
      0,
      0,
      'tooltip-body-bottom'
    )
  ];
  visible = false;
  constructor(public cdr: ChangeDetectorRef) {}
  overlayRef: any;
  ngOnInit(): void {}
  ngAfterViewInit() {
    fromEvent<any>(this.tooltip.nativeElement, 'mouseenter').subscribe(
      (event: any) => {
        this.isActive = true;
        this.cdr.markForCheck();
      }
    );
    fromEvent<any>(this.tooltip.nativeElement, 'mouseleave').subscribe(
      (event: any) => {
        this.isActive = false;
        this.cdr.markForCheck();
        this.mouseleave.emit();
      }
    );
  }
  setOverlayOrigin(origin: CdkOverlayOrigin): void {
    this.origin = origin;
    this.show();
  }
  hide(): void {
    this.visible = false;
    this.cdr.markForCheck();
  }
  show(): void {
    this.visible = true;
  }
  setTitle(title: string): void {
    this.title = title;
  }
  setView(view): void {
    this.view = view;
  }
  positionChange($event) {
  }
}
Результат работы
Angular Tooltip CDKЗаключениеМы изучили Material CDK Overlay. Сделали Tooltip и познакомились с некоторыми важными API Angular. Эта статья поможет вам создавать свои прекрасные компоненты на базе CDK. А в следующей части мы рассмотрим создание других компонентов на базе CDK.К примеру нужно относиться как к заготовке и ознакомлением с CDK Overlay. Для того чтобы использовать его в проекте, нужно будет доработать следующие моменты:
  • отписаться от событий;
  • покрыть тестом;
  • прикрутить палитру из ваших переменных темы.

===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_angular, #_angular, #_frontend, #_material, #_blog_kompanii_evroplan (
Блог компании Европлан
)
, #_angular
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 25-Ноя 01:43
Часовой пояс: UTC + 5