Angular интеграция

Для использования Popper.js в приложениях на Angular необходимо сначала установить библиотеку через npm:

npm install @popperjs/core

После установки Popper.js можно импортировать в компоненты или сервисы Angular. Обычно создают сервис для управления Popper-элементами, чтобы централизовать логику позиционирования и избежать дублирования кода.

import { Injectable } from '@angular/core';
import { createPopper, Instance, Placement } from '@popperjs/core';

@Injectable({
  providedIn: 'root'
})
export class PopperService {
  private popperInstance: Instance | null = null;

  createPopper(reference: HTMLElement, popper: HTMLElement, placement: Placement = 'bottom') {
    this.destroyPopper();
    this.popperInstance = createPopper(reference, popper, {
      placement,
      modifiers: [
        { name: 'offset', options: { offset: [0, 8] } },
        { name: 'preventOverflow', options: { boundary: 'viewport' } }
      ]
    });
  }

  update() {
    this.popperInstance?.update();
  }

  destroyPopper() {
    this.popperInstance?.destroy();
    this.popperInstance = null;
  }
}

Такой сервис позволяет создавать, обновлять и уничтожать Popper-элементы без прямой зависимости от DOM в компонентах, что соответствует архитектуре Angular.


Использование Popper.js в компонентах

Пример использования Popper.js в Angular-компоненте с кнопкой и всплывающей подсказкой:

import { Component, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
import { PopperService } from './popper.service';

@Component({
  selector: 'app-tooltip',
  template: `
    <button #button>Навести</button>
    <div #tooltip class="tooltip">Подсказка</div>
  `,
  styles: [`
    .tooltip {
      background: #333;
      color: #fff;
      padding: 8px 12px;
      border-radius: 4px;
      display: none;
      position: absolute;
    }
  `]
})
export class TooltipComponent implements AfterViewInit, OnDestroy {
  @ViewChild('button', { static: true }) button!: ElementRef<HTMLButtonElement>;
  @ViewChild('tooltip', { static: true }) tooltip!: ElementRef<HTMLDivElement>;

  constructor(private popperService: PopperService) {}

  ngAfterViewInit() {
    this.button.nativeElement.addEventListener('mouseenter', () => {
      this.tooltip.nativeElement.style.display = 'block';
      this.popperService.createPopper(this.button.nativeElement, this.tooltip.nativeElement, 'top');
    });

    this.button.nativeElement.addEventListener('mouseleave', () => {
      this.tooltip.nativeElement.style.display = 'none';
      this.popperService.destroyPopper();
    });
  }

  ngOnDestroy() {
    this.popperService.destroyPopper();
  }
}

Ключевые моменты:

  • Использование ViewChild позволяет работать напрямую с DOM-элементами внутри Angular.
  • Popper создаётся при наведении и уничтожается при уходе курсора, что минимизирует расход ресурсов.
  • placement можно менять динамически для разных вариантов отображения.

Работа с динамическим контентом

Для динамического контента, например, при изменении размеров подсказки, необходимо вызывать update() после любых изменений DOM:

this.tooltip.nativeElement.textContent = 'Новый текст';
this.popperService.update();

Это гарантирует корректное позиционирование Popper-элемента в реальном времени.


Модификаторы и их применение

Popper.js использует модификаторы для управления поведением:

  • offset – смещение поппера относительно опорного элемента.
  • preventOverflow – предотвращает выход поппера за границы экрана.
  • flip – автоматически меняет позицию, если нет места в текущем направлении.
  • arrow – позволяет прикрепить стрелку к попперу.

Пример с модификатором стрелки:

this.popperService.createPopper(this.button.nativeElement, this.tooltip.nativeElement, 'bottom');
<div #tooltip class="tooltip">
  Подсказка
  <div data-popper-arrow></div>
</div>
[data-popper-arrow] {
  width: 10px;
  height: 10px;
  background: inherit;
  transform: rotate(45deg);
  position: absolute;
}

Адаптивность и реактивное позиционирование

Popper.js автоматически реагирует на изменение размеров окна и скролла, если использовать модификатор preventOverflow с опцией boundary: 'viewport'. Для сложных сценариев, когда контент динамически изменяется, рекомендуется использовать Angular ChangeDetectorRef вместе с вызовом update():

import { ChangeDetectorRef } from '@angular/core';

constructor(private cdr: ChangeDetectorRef, private popperService: PopperService) {}

updateTooltip() {
  this.tooltip.nativeElement.textContent = 'Обновлённая подсказка';
  this.cdr.detectChanges();
  this.popperService.update();
}

Интеграция с Angular CDK

Angular CDK (Component Dev Kit) предоставляет абстракции для оверлеев. Popper.js можно использовать внутри Angular CDK Overlay для более сложных компонентов:

import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';

const overlayRef: OverlayRef = this.overlay.create({
  positionStrategy: this.overlay.position()
    .flexibleConnectedTo(this.button.nativeElement)
    .withPositions([{ originX: 'center', originY: 'bottom', overlayX: 'center', overlayY: 'top' }])
});

overlayRef.attach(new ComponentPortal(TooltipComponent));

Popper.js можно комбинировать с Overlay для сложных всплывающих окон с динамическим позиционированием и анимациями.


Оптимизация производительности

  • Создавать Popper только при необходимости (например, при hover или click).
  • Использовать destroy() при уходе с экрана.
  • Минимизировать количество модификаторов для простых подсказок.
  • Для списков элементов с подсказками можно создать общий сервис Popper, чтобы избежать множественных экземпляров.

Резюме возможностей

  • Поддержка всех видов позиционирования: top, bottom, left, right, auto.
  • Управление смещением через offset.
  • Автоматический переворот при недостатке места с flip.
  • Реакция на изменение DOM и окна с update().
  • Возможность интеграции с Angular сервисами и CDK Overlay.
  • Поддержка стрелок и пользовательских модификаторов.

Popper.js в связке с Angular позволяет создавать сложные, динамические и адаптивные интерфейсы, обеспечивая точное позиционирование и контроль над поведением всплывающих элементов.