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.