'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 |
