Angular 2+ (or 14.2 in your case) is completely different to Angular JS. So a simple conversion can be very hard, as you must have noticed. There are some libraries which can help you to create a popover very easy, like Bootstrap (ng-bootstrap
). But if you wanna do it with the pure Angular 2 way to learn how it works then here it is! (Stackblitz)
I think for a popover Angulars Directive
is the way to go. A Directive
can manipulate its host component appearance or behavior. Read all about it here.
The follow Directive
does the follow: OnInit loads the ng-template
and show it on bottom to its parent. Some click listener handle backdrop clicks and/or the popovers state itself.
@Directive({
selector: "[popoverTrigger]"
})
export class PopoverDirective implements OnDestroy, OnInit {
@Input()
popoverTrigger!: TemplateRef<object>;
@Input()
closeOnClickOutside: boolean = false;
private unsubscribe = new Subject();
private overlayRef!: OverlayRef;
constructor(
private elementRef: ElementRef,
private overlay: Overlay,
private vcr: ViewContainerRef,
private popoverService: PopoverService
) {}
ngOnInit(): void {
this.createOverlay();
this.popoverService.getState().subscribe(resp => {
if (resp) {
this.detachOverlay();
}
});
}
ngOnDestroy(): void {
this.detachOverlay();
this.unsubscribe.next();
this.unsubscribe.complete();
}
@HostListener("click") clickou() {
this.attachOverlay();
}
private createOverlay(): void {
const scrollStrategy = this.overlay.scrollStrategies.block();
const positionStrategy = this.overlay.position().connectedTo(
this.elementRef,
{ originX: "start", originY: "bottom" },
{ overlayX: "start", overlayY: "top" }
//ToDo entender como funciona o posicionamento
// new ConnectionPositionPair(
// { originX: "start", originY: "bottom" },
// { overlayX: "start", overlayY: "bottom" }
// ),
// new ConnectionPositionPair(
// { originX: "start", originY: "bottom" },
// { overlayX: "start", overlayY: "bottom" }
// )
);
this.overlayRef = this.overlay.create({
positionStrategy,
scrollStrategy,
hasBackdrop: true,
backdropClass: ""
});
this.overlayRef
.backdropClick()
.pipe(takeUntil(this.unsubscribe))
.subscribe(() => {
if (this.closeOnClickOutside) {
this.detachOverlay();
}
});
}
private attachOverlay(): void {
if (!this.overlayRef.hasAttached()) {
const periodSelectorPortal = new TemplatePortal(
this.popoverTrigger,
this.vcr
);
this.overlayRef.attach(periodSelectorPortal);
}
}
private detachOverlay(): void {
if (this.overlayRef.hasAttached()) {
this.overlayRef.detach();
}
}
}
So.. a lot of code. But write only once and use it for all components you want. Now in your component, like app.component.ts, you need to set this in the html:
<p [popoverTrigger]="popoverVazio">
Popover no texto
</p>
<ng-template #popoverVazio>
<popover-container>
<p>exemplo de popovere acionado por um texto</p>
<button mat-raised-button color="primary" (click)="closePopover()">
Fechar
</button>
</popover-container>
</ng-template>
That's all. The ng-template
is not shown. Only if the popup open. A ng-template
, by the way, is also always not shown by default. If you set ngIf="true"
to it (as example) then it will be visible. More about it here.