Search code examples
javascriptangularfunctionfullcalendarcustom-events

Angular FullCalendar add Button with method to Event


Context: Fullcalendar v5 - Angular 13

I need to include in each event a dynamic button to perform a method (myfunction) which is placed in the typescript file.

In the calendarOptions I can work on the eventContent to add a simple html such as a button:

  calendarOptions: CalendarOptions = {
...
    eventContent: { html: '<button mat-button>mybutton</button>' }
}

I can also include a javascript method like an alert including it in the eventContent:

   eventContent: { html: '<button mat-button onclick="alert(\'Hello\')">mybutton</button>' },

This also works, BUT I would like to trigger a method which is in my typescript model and not a native JS function.

When I try with

eventContent: { html: '<button mat-button onclick="myfunction()">mybutton</button>' },

this is not working...

and I tried obviously

eventContent: { html: '<button mat-button (click)="myfunction()">mybutton</button>' },

which is not even seeing the (click).

Apparently the angular compiler is not working here....so how can I refer to that method?

I read this Custom button for each event in ui-calendar fullcalendar BUT that is not Angular and by the way the eventRender seems to be obsolete and not present anymore in the v5 now.

This (through eventClick) is not working either:

    eventClick: function (info) {
      this.myfunction(info.event.id);
   }

I finally tried:

views: {
  timeGridWeek: {  //questo modifica TUTTI gli eventi in questa vista
    
    eventContent: function (event: any, element: any) {
      
      let eventWrapper = document.createElement('div');
      eventWrapper.addEventListener("click",function(event){ console.log(event); })
    
      eventWrapper.innerText = 'test dayGridWeek';
      var arrayOfDomNodes = [eventWrapper];

      return { domNodes: arrayOfDomNodes };
    }
  },
},

But again, this works for a console.log, not for a custom method/function of my model.

In the end how can I launch the myfunction from a dynamically added button?


Solution

  • This is how I managed to solve it.

    CalendarOptions is an object like any other object. ContentEvent is a nested object hierarchically placed inside CalendarOptions.

    I had written:

    this.CalendarOptions.eventContent = 
    function (arg: any)
            {   //add time
                let timeText = document.createElement('div')
                  timeText.className = "fc-event-time";
                  timeText.innerHTML = arg.timeText;
                //include additional info
                let additionalText = document.createElement('i')
                  additionalText.className = "fc-event-title";
                  additionalText.innerHTML = arg.event.extendedProps.myproperty;
                //include img/icon that should work as a button
                let img = document.createElement('img');
                  img.src = '../../assets/sign_YES.svg';
                  img.className = "_iconFirma";
                
    
                img.addEventListener("click", function (e: Event) {
                  e.stopPropagation();  //prevents eventClick to be triggered
                  this.anotherMethod();  //this is not working because here 'this' is the function
                  
                })
    
                let arrayOfDomNodes = [ timeText, additionalText, img ]
                return { domNodes: arrayOfDomNodes }
              
            }
    

    In this way "this" could not be used as a reference to the component.

    This is how I refactored it, using arrow functions in order to "bring" the component to be "callable" through "this":

    this.calendarOptions.eventContent = 
        (arg: any) =>
            {   //add time
                let timeText = document.createElement('div')
                  timeText.className = "fc-event-time";
                  timeText.innerHTML = arg.timeText;
                //include additional info
                let additionalText = document.createElement('i')
                  additionalText.className = "fc-event-title";
                  additionalText.innerHTML = arg.event.extendedProps.myproperty;
                //include img/icon that should work as a button
                let img = document.createElement('img');
                  img.src = '../../assets/sign_YES.svg';
                  img.className = "_iconFirma";
                
    
                img.addEventListener("click", (e: Event) => {
                  e.stopPropagation(); //prevents eventClick to be triggered
                  this.anotherMethod(); //this works now because 'this' is not the function anymore: it's the angular component
                  
                })
    
                let arrayOfDomNodes = [ timeText, additionalText, img ]
                return { domNodes: arrayOfDomNodes }
              
            }
    

    This switch from

    function (argument) { //here 'this' is the function }
    

    to

    (argument) => { //here 'this' is the component}
    

    (flat-arrow function) makes it completely different and lets "this" be the component. This different approach must be used both in the first and in the second function call.

    In this way I could add an image/icon inside each event, and make it work as a button

    enter image description here