最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - how to programmatically add ngBootstrap popover to element? - Stack Overflow

programmeradmin2浏览0评论

I am currently working on a calendar in my angular v8 application.

This is the plugin I use:

this is the ponent which I include in my html template:

    <full-calendar
        defaultView="dayGridMonth"
        [editable]="true"
        [eventLimit]="5"
        [nowIndicator]="true"
        [slotLabelFormat]="timeFormat"
        [eventTimeFormat]="timeFormat"
        [eventClassName]="'fc-event-brand'"
        [minTime]="'08:00:00'"
        [maxTime]="'24:00:00'"
        [header]="{
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth, timeGridWeek, timeGridDay, listWeek'
        }"
        [plugins]="calendarPlugins"
        [events]="calendarEvents"
        (eventMouseEnter)="showPopover($event)"
        (eventMouseLeave)="hidePopover($event)"
        (eventRender)="renderTooltip($event)"></full-calendar>

But how can I add a ngBootstrap popover or tooltip to an element?

this is the renderTooltip():

renderTooltip(event) {
    // bind ngBootstrap tooltip or popover to $event.el
}

I am currently working on a calendar in my angular v8 application.

This is the plugin I use: https://fullcalendar.io

this is the ponent which I include in my html template:

    <full-calendar
        defaultView="dayGridMonth"
        [editable]="true"
        [eventLimit]="5"
        [nowIndicator]="true"
        [slotLabelFormat]="timeFormat"
        [eventTimeFormat]="timeFormat"
        [eventClassName]="'fc-event-brand'"
        [minTime]="'08:00:00'"
        [maxTime]="'24:00:00'"
        [header]="{
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth, timeGridWeek, timeGridDay, listWeek'
        }"
        [plugins]="calendarPlugins"
        [events]="calendarEvents"
        (eventMouseEnter)="showPopover($event)"
        (eventMouseLeave)="hidePopover($event)"
        (eventRender)="renderTooltip($event)"></full-calendar>

But how can I add a ngBootstrap popover or tooltip to an element?

this is the renderTooltip():

renderTooltip(event) {
    // bind ngBootstrap tooltip or popover to $event.el
}
Share Improve this question edited Dec 22, 2020 at 10:18 Zoe - Save the data dump 28.3k22 gold badges128 silver badges160 bronze badges asked Jul 30, 2019 at 19:31 SireiniSireini 4,26213 gold badges55 silver badges96 bronze badges 1
  • you can see the demo here : link search for 'tooltip' in page source and you will see the implementation. – Mazdak Commented Aug 4, 2019 at 10:49
Add a ment  | 

1 Answer 1

Reset to default 12 +150

I would create one simple ponent which is basically just a popover wrapper:

@Component({
  template: `
    <div class="fc-content" [ngbPopover]="template" container="body" triggers="manual">
      <ng-content></ng-content>
    </div>
  `,
})
export class PopoverWrapperComponent {
  template: TemplateRef<any>;

  @ViewChild(NgbPopover, { static: true }) popover: NgbPopover;
}
  • template property will be passed from our main ponent so we can create any template we want
  • we also hold of NgbPopover instance so we can use popover.open(context) later.

Also make sure you've added this ponent to entryComponents array of your NgModule:

@NgModule({
  imports:      [ BrowserModule, FullCalendarModule, NgbPopoverModule ],
  declarations: [ AppComponent, PopoverWrapperComponent ],
  entryComponents: [PopoverWrapperComponent],
                              \/
                           like this
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

Now we're going to dynamically render this ponent into $event.el element and project child nodes in ng-content.

import { Component, ComponentRef,
 TemplateRef, ViewChild, ComponentFactoryResolver, 
 Injector, ApplicationRef } from '@angular/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import { NgbPopover } from '@ng-bootstrap/ng-bootstrap';


@Component({
  selector: 'my-app',
  templateUrl: './app.ponent.html',
  styleUrls: ['./app.ponent.css']
})
export class AppComponent {
  calendarPlugins = [dayGridPlugin];

  calendarEvents = [
    { title: 'event 1', date: '2019-08-09', customProp1: 'customProp1', customProp2: 'customProp2' },
    { title: 'event 2', date: '2019-08-12', customProp1: 'customProp3', customProp2: 'customProp4' }
  ];

  @ViewChild('popoverTmpl', { static: true }) popoverTmpl: TemplateRef<any>;

  popoversMap = new Map<any, ComponentRef<PopoverWrapperComponent>>();

  popoverFactory = this.resolver.resolveComponentFactory(PopoverWrapperComponent);

  constructor(
    private resolver: ComponentFactoryResolver, 
    private injector: Injector,
    private appRef: ApplicationRef) {
  }

  renderTooltip(event) {
    const projectableNodes = Array.from(event.el.childNodes)

    const pRef = this.popoverFactory.create(this.injector, [projectableNodes], event.el);
    pRef.instance.template = this.popoverTmpl;

    this.appRef.attachView(pRef.hostView);
    this.popoversMap.set(event.el, pRef);
  }

  destroyTooltip(event) {
    const popover = this.popoversMap.get(event.el); 
    if (popover) {
      this.appRef.detachView(popover.hostView);
      popover.destroy();
      this.popoversMap.delete(event.el);
    }
  }

  showPopover(event) {
    const popover = this.popoversMap.get(event.el);
    if (popover) {
      popover.instance.popover.open({ event: event.event });
    }
  }

  hidePopover(event) {
    const popover = this.popoversMap.get(event.el);
    if (popover) {
      popover.instance.popover.close();
    }
  }
}

The key part here is how we're rendering ponent dynamically with projectable nodes:

renderTooltip(event) {
  const projectableNodes = Array.from(event.el.childNodes)

  const pRef = this.popoverFactory.create(this.injector, [projectableNodes], event.el);
  pRef.instance.template = this.popoverTmpl;

  this.appRef.attachView(pRef.hostView)
  this.popoversMap.set(event.el, pRef)
}

Rendered dynamically ponent has no relation to the Angular change detection tree so we have to add its view to ApplicationRef views so change detection should work there.

Make sure you've subscribed to the following event in your template:

(eventRender)="renderTooltip($event)"
(eventDestroy)="destroyTooltip($event)"
(eventMouseEnter)="showPopover($event)"
(eventMouseLeave)="hidePopover($event)"

You should also define template for popover, i.e.:

<ng-template #popoverTmpl let-event="event">
  <h6>{{ event.title }}</h6>
  <div>
    <p>{{ event.extendedProps.customProp1 }}</p>
    <p>{{ event.extendedProps.customProp2 }}</p>
  </div>
</ng-template>

Stackblitz Example

Stackblitz Example with @fullcalendar/angular/5.5.0

发布评论

评论列表(0)

  1. 暂无评论