问题
What i try to achieve is to extend ngbPopover directive and wrap in custom directive all this proeprties so instead of showing them just to include my directive.
For example i am using ngbpopover like this:
<button
type="button"
class="btn btn-primary mb-4"
placement="right-top"
[ngbPopover]="popOver"
popoverClass="popover-custom"
#popOverRef="ngbPopover"
triggers="manual"
[autoClose]="false"
(click)="popOverRef.toggle()"
(hidden)="onPopoverClose(options)"
>
Standard Picker
</button>
<ng-template #popOver>
<popover [data]="options" [popover]="popOverRef"><popover>
</ng-template>
My goal at the end is to have something like:
<button type="button" class="btn btn-primary mb-4" customDirective></button>
so all this props to be handled inside customDirective. Tried to extend ngbPopoverDirective as code below but i struggle to show popover with this approach. Is this doable or some other idea?
Custom directive:
import {
Directive,
ElementRef,
Renderer2,
Injector,
ComponentFactoryResolver,
ViewContainerRef,
NgZone,
Inject,
ChangeDetectorRef,
ApplicationRef,
Input,
OnInit,
} from '@angular/core';
import { NgbPopover, NgbPopoverConfig } from '@ng-bootstrap/ng-bootstrap';
import { DOCUMENT } from '@angular/common';
@Directive({
selector: '[popover]'
})
export class PopoverDirective extends NgbPopover implements OnInit {
@Input()
popover: string;
config: NgbPopoverConfig = {
autoClose: false,
placement: 'right-top',
triggers: "manual",
container: 'body',
disablePopover: false,
popoverClass: 'popover-custom',
openDelay: 0,
closeDelay: 0
}
constructor(
_elementRef: ElementRef<HTMLElement>,
_renderer: Renderer2,
injector: Injector,
componentFactoryResolver: ComponentFactoryResolver,
viewContainerRef: ViewContainerRef,
config: NgbPopoverConfig,
_ngZone: NgZone,
@Inject(DOCUMENT) _document: any,
_changeDetector: ChangeDetectorRef,
applicationRef: ApplicationRef
) {
super(
_elementRef,
_renderer,
injector,
componentFactoryResolver,
viewContainerRef,
config,
_ngZone,
_document,
_changeDetector,
applicationRef
);
console.log("here");
}
ngOnInit() {
console.log("onInit")
}
}
回答1:
Instead of creating a directive, you could also create a component with the selector my-popover.
This component will hold your HTML template code:
<button
type="button"
class="btn btn-primary mb-4"
placement="right-top"
[ngbPopover]="popOver"
popoverClass="popover-custom"
#popOverRef="ngbPopover"
triggers="manual"
[autoClose]="false"
(click)="popOverRef.toggle()"
(hidden)="onPopoverClose(options)"
>
Standard Picker
</button>
<ng-template #popOver>
<popover [data]="options" [popover]="popOverRef"><popover>
</ng-template>
And then finally to use that, you could do the following:
<my-popover></my-popover>
If you need to interact with that component, e.g. to listen to the click events have a look at input and output bidings of components.
Hope this helps!
For more information on the difference between components and directives, you could check this question.
回答2:
Ok finally got this to work not sure why my previous code was not working. Anyway i succeed with extending ngbPopover directive. In my case i wanted popoup to be open on parent click for example button, this can be change to hover (mouseenter).... etc..
import {
ElementRef,
Directive,
Input,
TemplateRef,
EventEmitter,
Renderer2,
Injector,
ComponentFactoryResolver,
ViewContainerRef,
NgZone,
OnInit,
OnDestroy,
Inject,
ChangeDetectorRef,
ApplicationRef,
} from '@angular/core';
import { NgbPopover, NgbPopoverConfig } from '@ng-bootstrap/ng-bootstrap';
import { DOCUMENT } from '@angular/common';
@Directive({
selector: '[customPopover]',
exportAs: 'customPopover',
})
export class PopoverDirective extends NgbPopover implements OnInit {
@Input() customPopover: TemplateRef<any>;
constructor(
private _elRef: ElementRef,
private _render: Renderer2,
injector: Injector,
componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef,
config: NgbPopoverConfig,
ngZone: NgZone,
private changeRef: ChangeDetectorRef,
@Inject(DOCUMENT) _document: any,
applicationRef: ApplicationRef
) {
super(
_elRef,
_render,
injector,
componentFactoryResolver,
viewContainerRef,
config,
ngZone,
_document,
changeRef,
applicationRef
);
this.triggers = 'manual';
this.popoverTitle = '';
this.container = 'body';
this.popoverClass = 'popover-custom';
this.autoClose = false;
}
ngOnInit(): void {
super.ngOnInit();
this.ngbPopover = this.customPopover;
// we need to listen on parent element click envet for example button
// and toggle popover
this._render.listen(this._elRef.nativeElement.parentNode, 'click', () => {
this.toggle();
});
}
ngOnDestroy(): void {
super.ngOnDestroy();
}
}
component that use this directive:
<ng-template #popTemplate>
<custom-content-comp [data]="test" [popover]="popOverRef"></custom-content-comp>
</ng-template>
<span [customPopover]="popTemplate" #popOverRef="customPopover" placement="right-top" triggers="manual">
<ng-content></ng-content>
</span>
For popTemplate i am using again custom component this can be whatever we want either string either custom template.
Usage:
<div class="col-3">
<br />
<button
type="button"
class="btn btn-primary mb-4"
customPopoverComp
>
Standard Picker
</button>
</div>
来源:https://stackoverflow.com/questions/61010439/extend-ng-boostrap-popover-component-with-custom-angular-directive