How can I dynamically delete events from this PrimeNG FullCalendar (clicking on an event)?

Viewed 922

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/

So I have a calendar like this:

enter image description here

What I am trying to implement at this stage is that when the user click on a specific event, all the events into this calendar will be deleted (in a second stage I will implement that only the specific clicked event will be deleted, but at the moment I am keeping the logic as simple as possible).

So this is the class controlling my calendar 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;

  calendar:any;


  constructor(private eventService: EventService) {}

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

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

      //let events = this.calendar.getEvents();
      //events[

      console.log("EVENTS: " + this.events);

      if(hour === 7) {
        event['backgroundColor'] = 'red';
      }
      else if(hour === 15) {
        event['backgroundColor'] = 'green';
      }
      else if(hour === 23) {
        event['backgroundColor'] = '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: '09:00:00',
        allDayDefault: false,

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

        eventClick: (eventClickEvent) => {
          console.log("EVENT CLICKED !!!");
          console.log(eventClickEvent.event.id);
          //this.eventService.removeEventById(eventClickEvent.event.id);
          this.eventService.removeEventById(eventClickEvent.event.id).subscribe(events => console.log(events));


            /*
            events => { this.events = events.map((event) => {
            var date = new Date(event.start);
            var hour = date.getHours();

            console.log("EVENTS: " + this.events);

            if(hour === 7) {
              event['backgroundColor'] = 'red';
            }
            else if(hour === 15) {
              event['backgroundColor'] = 'green';
            }
            else if(hour === 23) {
              event['backgroundColor'] = 'black';
            }

            return event;
          })});
          */
        },

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

        eventReceive: (eventReceiveEvent) => {
          console.log(eventReceiveEvent);
          //eventReceiveEvent.event.setAllDay(false, {maintainDuration: true});



          //eventReceiveEvent['backgroundColor'] = 'red';
          this.eventService.addEvent(eventReceiveEvent);
        }
    };

  }

  ngAfterViewInit() {

    this.calendar = this.fullcalendar.getCalendar();
    console.log("CALENDAR: " + this.calendar);

  }

}

As you can see into the ngOnInit() method I am subscribing the getEvents() method of a service that return the list of rendered events as Observable (I am doing in this way because in the returned output I am handling different color for different events type, but this is not strictly related to my question...it works fine).

So, as you can see this component class contains this hook method eventClick: (eventClickEvent) handling the click on an event of my calendar. I was thinking to call a delete events method defined into my service and render it again but it is not working (I have done several try and it is not working). I am also not sure that this is the correct approach to the problem.

From what I can understand into the ngOnInit() method I have the subscription to my calendar event array, so chainging the content of this array this change is published and it should rendered again, I tried also this but it is not working...so I think that probably I am missing something in the Angular logic.

This is the code of my service class:

import { Injectable } from '@angular/core';
import { HttpClientModule, HttpClient } from '@angular/common/http'
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable()
export class EventService {

  private events = [];

  /*
  private events = [
    {id: 1, title: 'All Day Event', start: '2017-02-01'},
    {id: 2, title: 'Long Event', start: '2017-02-07', end: '2017-02-10'},
    {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(): Observable<any[]> {
    return this.eventData.asObservable();
  }


  addEvent(event) {
    //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]);
    //console.log(this.events);
  }

  removeEventById(id:number): Observable<any[]>  {
    console.log("EVENTDATA: " + this.eventData.getValue());

    return this.eventData.asObservable();
  }


  /*
  removeEventById = (id:number) => {
    this.events = [];
    //return this.events.filter(each=>each.id!=id);

  }
  */

  getPeople(): Promise<any[]> {
    return Promise.all(this.people)
        .then(res => {
          console.log(res);
          return res;
        })
  }

}

So what is wrong? What am I missing? What could be a smart solution to correctly handle my problem?

3 Answers

primeng fullcalendar developed from fullcalendar.io, you can filter data then assign to eventData. I created your code in Demo

 this.eventService.removeEventById(eventClickEvent.event.id).subscribe(events => console.log(events));

in your method

removeEventById(id:number): Observable<any[]>  {
    this.events=this.events.filter(each=>each.id!=id);
    this.eventData.next([...this.events]);
    return this.eventData.asObservable();
}

As you are using Observables, you can use the map operator combined with Array.prototype.filter:

import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

removeEventById(id:number): Observable<any[]>  {    
    return this.getEvents()
        .pipe(
            map(items => items.filter(item => item.id == id))
        );    
} 

The whole work example can be seen at the stackblitz.

So your service could look like this:

@Injectable()
export class MyServices {
    private events = [
        {   
          id: 1,
          title: 'Foo 1',
          date: '2020-06-07'
        },
        {
          id: 2,
          title: 'Foo 2',
          date: '2020-06-08'
        },
        {
          id: 3,
          title: 'Foo 3',
          date: '2020-06-05'
        }
    ];

    public eventData = new BehaviorSubject(this.events);

    constructor() { }

    sayHello() {        
        console.log("Say Hello");
    }

    getEvents(): Observable<any[]> {
        return this.eventData.asObservable();
    }


  addEvent(event) {
    const newEvent = {id: 5, title: event.event.title, 
        start: event.event.start, end: event.event.end};
    this.events.push(newEvent);
    this.eventData.next(Object.assign({}, this.events));
  }

  removeEventById(id:number): Observable<any[]>  {    
    return this.getEvents()
      .pipe(
        map(items => items.filter(item => item.id == id))
      );    
  }    
}

and your.component.ts could look like this:

export class AppComponent implements OnInit {
    options: any;
    eventsModel: any;
    @ViewChild('fullcalendar') fullcalendar: FullCalendarComponent;
    @ViewChild('external') external: ElementRef;

    constructor(private myServices: MyServices){
        this.myServices.sayHello();
    }

    eventClick(model) {
        this.myServices.removeEventById(model.id)
            .subscribe(s => this.eventsModel = s);       
    }

So there you few things that should be in place.

  1. For Removing all events.

    In events.service

public deleteEvents() {
   this.events = [];
   this.eventData.next(this.events);
 }

In fullCalendar.component.ts fullCalendar options :-

eventClick: (eventClickEvent) => {
      console.log("EVENT CLICKED !!!");
      this.eventService.deleteEvents();
    }
  1. For Removing specific events.

First in your calendar a unique id should be present for each event. Currently in your addEvent method you are giving same Id to every event. i.e. 5. First lets fix that.

public currentId = 1;

public addEvent() {
      const newEvent = {id: this.currentId++, title: event.event.title, start: event.event.start, end: event.event.end};
   //Remaining Add Event Code.
}

This way you will have a unique id to event, whenever it will be added to your calendar.

Now on event clicked you are calling a method, i.e. removeEventById(). That should be written as :-

removeEventById(id): Observable<any[]>  {
    this.events = this.events.filter((event) => event.id!==+id);
    this.eventData.next(this.events);
    return this.eventData.asObservable();
}

Note :- the id you receive from event click object is in string and the id you have in array is number. Hence i have added + above in id received to parse it in number to match id and remove necessary item. without it the removal will not work.

In fullCalendar.component.ts fullCalendar options :-

eventClick: (eventClickEvent) => {
      console.log("EVENT CLICKED !!!");        
 this.eventService.removeEventById(eventClickEvent.event.id).subscribe(events => console.log(events));
    }
Related