Search code examples
javascriptangularfullcalendarprimengprimeng-calendar

How to dinamically change Angular FullCalendar event color (of a specific event)?


I am working on an Angular application using PrimeNG Full Calendar component, this one: https://primefaces.org/primeng/showcase/#/fullcalendar

That is based on the Angular FullCalendar component, this one: https://fullcalendar.io/

Here you can find my entire code: https://bitbucket.org/dgs_poste_team/soc_calendar/src/master/

I am finding some difficulties trying to dinamically change the background color of the event rendered on my calendar. I have to have different event background color based on different event information (the start event time, for example: if an event start at 07:00 is green, if it start at 15:00 it is red, if it start at 23:00 it is blue, but this logic is not important at this time).

In my project I am dragging external event into my calendar, something like this: https://fullcalendar.io/docs/external-dragging-demo

So what I want is that when I drag an event into my calendar its background will have a specific color based on the startime.

So, as you can see in my BitBucket repository I have this FullcalendarComponent handling the component that contains the calendar that recives events from an external component:

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { EventService } from '../event.service';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import listPlugin from '@fullcalendar/list';
import interactionPlugin, { Draggable } from '@fullcalendar/interaction';
import { FullCalendar } from 'primeng';

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

  events: any[];
  options: any;
  header: any;

  //people: any[];

  @ViewChild('fullcalendar') fullcalendar: FullCalendar;


  constructor(private eventService: EventService) {}

  ngOnInit() {
    this.eventService.getEvents().then(events => { this.events = events;});

    this.options = {
        plugins:[ dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin ],
        defaultDate: '2017-02-01',
        header: {
            left: 'prev,next',
            center: 'title',
            right: 'dayGridMonth,timeGridWeek,timeGridDay'
        },
        editable: true,
        nextDayThreshold: '06:00:00',
        //eventColor: '#378006',

        dateClick: (dateClickEvent) =>  {         // <-- add the callback here as one of the properties of `options`
          console.log("DATE CLICKED !!!");
        },

        eventClick: (eventClickEvent) => {
          console.log("EVENT CLICKED !!!");
        },

        eventDragStop: (eventDragStopEvent) => {
          console.log("EVENT DRAG STOP !!!");
        },

        eventReceive: (eventReceiveEvent) => {
          console.log(eventReceiveEvent);
          eventReceiveEvent.event.setAllDay(false, {maintainDuration: true});
          eventReceiveEvent.eventColor = '#378006';
          eventReceiveEvent.event.eventColor = '#378006';
          eventReceiveEvent.event.css('background-color', '#378006');
          this.eventService.addEvent(eventReceiveEvent);
        }
    };

  }

}

I discovered that adding this option eventColor: '#378006', I can change the default event background color...but in this way it is static and I can't handle different color for different type of events (I simply change the default color for all the event, so it is not good for my use case).

I have this method that is used to revice the events dragged into my calendar:

eventReceive: (eventReceiveEvent) => {
  console.log(eventReceiveEvent);
  eventReceiveEvent.event.setAllDay(false, {maintainDuration: true});
  eventReceiveEvent.eventColor = '#378006';
  eventReceiveEvent.event.eventColor = '#378006';
  eventReceiveEvent.event.css('background-color', '#378006');
  this.eventService.addEvent(eventReceiveEvent);
}

and I was thinking that it could be a good candidate place where to put this behavior...as you can see in my code I tried to use some method to set the event color but it is not working...I still obtain the defaul event color when my page is rendered.

Why? What is wrong? What am I missing? How can I obtain the desired behavior and set the event color by code?


Solution

  • Every event supports a property backgroundColor to decide what should be the background color of your event.

    Check here :- https://fullcalendar.io/docs/event-object

    To make this color decision automatic whenever a event is added, i changed promise in your service to observable. whenever you will push new event through your addevent method of service. your subscription in ngoninit of component will receive updated data and apply background colors. This will also prevent your original data from changing, as only your component needs this color to display. Not your original data.

    Changes in service :-

      1. public eventData = new BehaviorSubject(this.events);
      2. getEvents(): Observable<any[]> {
          return this.eventData.asObservable();
         }
      3. addEvent(event) {
          //this.events.push(event);
          //console.log("EVENT:")
          //console.log(event.event.title);
          console.log(event.event.start);
          console.log(event);
          const newEvent = {id: 5, title: event.event.title, start: event.event.start, end: event.event.end};
          this.events.push(newEvent);
          this.eventData.next([...this.events]);
         }
    

    Complete Service After Changes :-

    import { Injectable } from '@angular/core';
    //import { Http } from '@angular/http';
    import { HttpClientModule, HttpClient } from '@angular/common/http'
    import { BehaviorSubject, Observable } from 'rxjs';
    
    @Injectable()
    export class EventService {
    
      private events = [
        {id: 1, title: 'All Day Event', start: '2017-02-01'},
        {id: 2, title: 'Long Event', start: '2017-02-07', end: '2017-02-10T7:00:00'},
        {id: 3, title: 'Repeating Event', start: '2017-02-09T16:00:00'},
        {id: 3, title: 'test', start: '2017-02-20T07:00:00'},
      ];
    
      private people = [
        {id: 1, name: "PERSONA 1"},
        {id: 2, name: "PERSONA 2"},
        {id: 3, name: "PERSONA 3"},
        {id: 4, name: "PERSONA 4"},
        {id: 5, name: "PERSONA 5"},
      ]
    
      public eventData = new BehaviorSubject(this.events);
    
    
      constructor(private http: HttpClient) {}
    
      /*
      getEvents(): Promise<any[]> {
        return this.http.get('assets/json_mock/calendarevents.json')
          .toPromise()
          .then(res => JSON.parse(JSON.stringify(res)).data)
          .then(res => {
            console.log(res);
            // you returned no value here!
            return res;
          })
      }
      */
    
    getEvents(): Observable<any[]> {
      return this.eventData.asObservable();
    }
    
    addEvent(event) {
      //this.events.push(event);
      //console.log("EVENT:")
      //console.log(event.event.title);
      console.log(event.event.start);
      console.log(event);
      const newEvent = {id: 5, title: event.event.title, start: event.event.start, end: event.event.end};
      this.events.push(newEvent);
      this.eventData.next([...this.events]);
    }
    
    getPeople(): Promise<any[]> {
      return Promise.all(this.people)
          .then(res => {
            console.log(res);
            return res;
          })
    }
    
    }
    

    In your fullcalendar.component.ts, you can change the color in observable where you are receiveing events from service like :-

    this.eventService.getEvents().subscribe(events => { this.events = events.map((event) => {
      var date = new Date(event.start);
      var hour = date.getHours();
      event['backgroundColor'] = hour === 5? 'red': (hour === 7 ? 'green' : 'black');
      return event;
    })});
    

    Output :-

    enter image description here

    Your complete fullcalendar.component.ts after changes:-

    import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
    import { EventService } from '../event.service';
    import dayGridPlugin from '@fullcalendar/daygrid';
    import timeGridPlugin from '@fullcalendar/timegrid';
    import listPlugin from '@fullcalendar/list';
    import interactionPlugin, { Draggable } from '@fullcalendar/interaction';
    import { FullCalendar } from 'primeng';
    
    @Component({
      selector: 'app-fullcalendar',
      templateUrl: './fullcalendar.component.html',
      styleUrls: ['./fullcalendar.component.css']
    })
    export class FullcalendarComponent implements OnInit {
      //backgroundColor
      events: any[];
      options: any;
      header: any;
    
      //people: any[];
    
      @ViewChild('fullcalendar') fullcalendar: FullCalendar;
    
    
      constructor(private eventService: EventService) {}
    
      ngOnInit() {
        this.eventService.getEvents().subscribe(events => { this.events = events.map((event) => {
          var date = new Date(event.start);
          var hour = date.getHours();
          event['backgroundColor'] = hour === 5? 'red': (hour === 7 ? 'green' : 'black');
          return event;
        })});
    
        this.options = {
            plugins:[ dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin ],
            defaultDate: '2017-02-01',
            header: {
                left: 'prev,next',
                center: 'title',
                right: 'dayGridMonth,timeGridWeek,timeGridDay'
            },
            editable: true,
            nextDayThreshold: '06:00:00',
    
            dateClick: (dateClickEvent) =>  {         // <-- add the callback here as one of the properties of `options`
              console.log("DATE CLICKED !!!");
            },
    
            eventClick: (eventClickEvent) => {
              console.log("EVENT CLICKED !!!");
            },
    
            eventDragStop: (eventDragStopEvent) => {
              console.log("EVENT DRAG STOP !!!");
            },
    
            eventReceive: (eventReceiveEvent) => {
              console.log(eventReceiveEvent);
              eventReceiveEvent.event.setAllDay(false, {maintainDuration: true});
              this.eventService.addEvent(eventReceiveEvent);
            }
        };
    
      }
    
    }
    

    I modified events time in service to below to achieve the output i have in image.

    private events = [
        {id: 1, title: 'All Day Event', start: '2017-02-01'},
        {id: 2, title: 'Long Event', start: '2017-02-07', end: '2017-02-10T7:00:00'},
        {id: 3, title: 'Repeating Event', start: '2017-02-09T16:00:00'},
        {id: 3, title: 'test', start: '2017-02-20T07:00:00'},
      ];