Для использования 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 в 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.placement можно менять динамически для разных вариантов
отображения.Для динамического контента, например, при изменении размеров
подсказки, необходимо вызывать update() после любых
изменений DOM:
this.tooltip.nativeElement.textContent = 'Новый текст';
this.popperService.update();
Это гарантирует корректное позиционирование Popper-элемента в реальном времени.
Popper.js использует модификаторы для управления поведением:
Пример с модификатором стрелки:
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 (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 для сложных всплывающих окон с динамическим позиционированием и анимациями.
destroy() при уходе с экрана.offset.flip.update().Popper.js в связке с Angular позволяет создавать сложные, динамические и адаптивные интерфейсы, обеспечивая точное позиционирование и контроль над поведением всплывающих элементов.