5

PrimeNG 7 introduced a <p-fullCalendar> component that wraps the FullCalendar 4.0 library as an Angular component. E.g.

<p-fullCalendar [options]="options" [events]="events"></p-fullCalendar>

The FullCalendar options include the eventRender option to specify a function to customise how events are rendered in the calendar. I've used it in the example below, but it generates DOM Elements using standard Javascript and therefore can't contain any other Angular components such as the Material design-styled button in the example (it still creates a basic unstyled button since the standard <button> is used).

I've tried looking into options such as using Angular's [innerHTML], ViewContainerRef with ComponentFactoryResolver or Angular's runtime JIT compiler, but I don't understand enough to get any of these approaches to work or to know which is best for this use case. How can I inject Angular components into the FullCalendar event entries?

It doesn't need to be fully dynamic, e.g. having hidden components that already exist on the page but are shown in the right location, or having ng-templates that are merely instantiated differently would possibly be fine so long as it displays and acts like a proper entry on the calendar.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-calendar',
  templateUrl: './calendar.component.html',
  styleUrls: ['./calendar.component.scss']
})
export class CalendarComponent implements OnInit {

  options: any;
  events: any[];

  constructor() { }

  ngOnInit() {
    this.events = [
        {
            "title": "All Day Event",
            "start": "2019-02-01"
        },
        {
            "title": "Long Event",
            "start": "2019-02-07",
            "end": "2019-02-10"
        },
        {
            "title": "Repeating Event",
            "start": "2019-02-09T16:00:00"
        },
        {
            "title": "Repeating Event",
            "start": "2019-02-16T16:00:00",
            'color': 'rgb(72,101,144)',
            'textColor': 'black',
        },
        {
            "title": "Conference",
            "start": "2019-02-11",
            "end": "2019-02-13"
        },
        {
            "title": "John Smith",
            "start": "2019-02-01T10:00:00"
        },
        {
          "title": "Appointment",
          "start": "2019-02-01T16:00:00"
        },
    ];

    this.refreshOptions();
  }

  refreshOptions() {
    this.options = { eventRender: this.onRender };
  }

  onRender(info: any) {
    let title = info.event.title;
    let start = info.event.start;
    let end = info.event.end;
    if (!info.event.allDay) {
      return createElementFromHTML(
        '<a class="fc-day-grid-event fc-event">' +
        '  <div class="fc-content">' +
        '    <span>' + event.title + '</span>' +
        '    <button mat-raised-button>Button</button>'+
        '  </div>'+
        '</a>');
    }
  }

}

// Use Javascript to generate DOM Elements    
function createElementFromHTML(htmlString) {
  var div = document.createElement('div');
  div.innerHTML = htmlString.trim();

  // Change this to div.childNodes to support multiple top-level nodes
  return div.firstChild; 
}
Silveri
  • 4,836
  • 3
  • 35
  • 44

1 Answers1

0

This might be related to what I came into just months ago. You can check out the answer I outlined here.

Using Angular's DomPortalHost, I was able to inject components into the anywhere in the DOM easily

EDIT: This is a sample snippet

import { ComponentFactoryResolver, ApplicationRef } from '@angular/core';
import { ComponentPortal, DomPortalHost } from '@angular/cdk/portal';

import { TheComponentThatIWantToAppend } from '../path-to-component-that-i-want-to-append'; 

constructor(
    protected componentFactoryResolver: ComponentFactoryResolver,
    protected appRef: ApplicationRef,
    protected injector: Injector
) {}
...

myfunction (): void {
    let myElementContainer = new DomPortalHost(
      eventRenderInfo.el.querySelector('.my-element-container'), // Target the element where you wish to append your component
      this.componentFactoryResolver,
      this.appRef,
      this.injector);

      myElementContainer.attach(new ComponentPortal(TheComponentThatIWantToAppend)); // This does the trick
}
Dreadnaut
  • 107
  • 1
  • 10