'How can I keep ngbpopovers open while being hovered on popover content?

I am using a ngbpopover to show some user information on a element, This popover need's to be trigger on hover and on click also. I can able to get in both cases. But I want to keep this popover open while the popover itself is being hovered, but it disappears as soon as the user stops hovering over the element. How can I achieve this?

              <a
                    class="popover"
                    #popRef="ngbPopover"
                    [ngbPopover]="popoverContent"
                    triggers="hover click"
                    (focus)="popRef.open()"
                    (focusout)="popRef.close()"
                    tooltip-hoverable="true"
                ></a>


Solution 1:[1]

I think you want autoclose="outside" also.

Solution 2:[2]

you need to write a separate directive for this. I tried a lot of code that was written 4-5 years ago but here is a complete solution (tested on angular 10 but should work above this v10+):

<p><span>Capacity Optimization</span>
  <i class="icon-info cursor-pointer" [appStickyPopover]="infoContent" placement="right auto"></i>
</p>
<ng-template #infoContent>
  <div class="help-tooltip">
    <p class="mb-0 font-size-14">Here you can enter a text</p>
    <div class="line"></div>
    <div class="text-left">
      <button class="btn btn-circle btn-primary btn-circle-width" type="button">
        <i class="icon-help"></i>
      </button>
      <a class="text-primary decoration-underline ml-3 font-size-14"
        (click)="openHelpLink()">Learn More</a>
    </div>
  </div>
</ng-template>

Here is the sticky-popover.directive.ts

import {
  ElementRef,
  Directive, Input, TemplateRef,
  EventEmitter,
  Renderer2,
  Injector,
  ComponentFactoryResolver,
  ViewContainerRef,
  NgZone,
  OnInit,
  OnDestroy,
  Inject, ChangeDetectorRef, ApplicationRef
} from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { NgbPopover, NgbPopoverConfig } from '@ng-bootstrap/ng-bootstrap';
@Directive({
  selector: '[appStickyPopover]',
  exportAs: 'appStickyPopover'
})
export class StickyPopoverDirective extends NgbPopover implements OnInit, OnDestroy {
  @Input() appStickyPopover: TemplateRef<any>;

  popoverTitle: string;

  // tslint:disable-next-line:max-line-length
  placement: 'auto' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom' | ('auto' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom')[];

  triggers: string;

  container: string;

  shown: EventEmitter<void>;

  hidden: EventEmitter<void>;
  ngpPopover: TemplateRef<any>;

  canClosePopover: boolean;
  toggle(): void {
    super.toggle();
  }

  isOpen(): boolean {
    return super.isOpen();
  }

  constructor(
    private _elRef: ElementRef,
    private _render: Renderer2,
    injector: Injector,
    componentFactoryResolver: ComponentFactoryResolver,
    private viewContainerRef: ViewContainerRef,
    config: NgbPopoverConfig,
    ngZone: NgZone,
    changeRef: ChangeDetectorRef,
    applicationRef: ApplicationRef,
    @Inject(DOCUMENT) _document: any) {
    super(_elRef, _render, injector, componentFactoryResolver, viewContainerRef, config, ngZone, _document, changeRef, applicationRef);
    this.triggers = 'manual';
    this.popoverTitle = '';
    this.container = 'body';
    this.openDelay = 300;
    this.closeDelay = 1500;
  }
  ngOnInit(): void {
    super.ngOnInit();
    this.ngbPopover = this.appStickyPopover;

    this._render.listen(this._elRef.nativeElement, 'mouseenter', () => {
      this.canClosePopover = false;
      this.open();
    });

    this._render.listen(this._elRef.nativeElement, 'mouseleave', (event: Event) => {
      this.canClosePopover = true;
      setTimeout(() => {
        if (this.canClosePopover) {
          this.close();
        }
      }, this.closeDelay);
    });

    this._render.listen(this._elRef.nativeElement, 'click', () => {
      this.close();
    });
  }

  ngOnDestroy(): void {
    super.ngOnDestroy();
  }

  open() {
    setTimeout(() => {
      super.open();

      const popover = window.document.querySelector('.popover');
      this._render.listen(popover, 'mouseover', () => {
        this.canClosePopover = false;
      });

      this._render.listen(popover, 'mouseout', () => {
        this.canClosePopover = true;
        setTimeout(() => {
          if (this.canClosePopover) {
            this.close();
          }
        }, this.closeDelay);
      });
    }, this.openDelay);
  }

  close() {
    super.close();
  }

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 wades
Solution 2 Alp Altunel