Search code examples
javascriptjqueryfullcalendarfullcalendar-scheduler

How to delete externally dropped events from fullcalendar?


I have been trying to make it so I can drag events from fullcalendar to the trash, and the code I have works great for events that are rendered when the calendar is initialized, but it doesn't seem to work with events that have been dropped onto the calendar from external events.

When I drag an external event onto the calendar, then try to drag them to the trash, it deletes all events.

I have seen a couple posts like this that say to use the _id attribute, but when I do that, nothing is deleted.

Here is the code I'm using:

eventDragStop: function (event, jsEvent) {

    var trashEl = jQuery('#trashCan');
    var ofs = trashEl.offset();

    var x1 = ofs.left;
    var x2 = ofs.left + trashEl.outerWidth(true);
    var y1 = ofs.top;
    var y2 = ofs.top + trashEl.outerHeight(true);

    if (jsEvent.pageX >= x1 && jsEvent.pageX <= x2 &&
        jsEvent.pageY >= y1 && jsEvent.pageY <= y2) {
        console.log(event);
        $('#calendar').fullCalendar('removeEvents', event._id);
    }
},

passing event.id works, but event._id, as I have it here, does nothing.

EDIT: To clarify things a bit, I decided I should leave all the relevant code.

This is how I have my external events. The first one is directly copied from a fullcalendar instance I've had working for about a year.:

<div id="external-events" class="dropdown col-lg-3">
    <button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1"
            data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
        Unscheduled Events
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
        <li class="dropdown-header">Drag events to calendar</li>
        <li class="divider"></li>
        <li><a href="#" data-duration="01:00" data-event='{"id": "803", "title": "Jennifer Ramundo"}' class='menu-event'>My Event 1</a></li>
        <li class="divider"></li>
        <li><a href="#" data-event="{'id': '2'}" class='menu-event'>My Event 2</a></li>
        <li class="divider"></li>
        <li><a href="#" data-event="{'id': '3'}" class='menu-event'>My Event 3</a></li>
    </ul>
</div>

This is how I initialize the external events:

$('#external-events .menu-event').each(function () {

    // store data so the calendar knows to render an event upon drop
    $(this).data('event', {
        title: $.trim($(this).text()), // use the element's text as the event title
        stick: true // maintain when user navigates (see docs on the renderEvent method)
    });

    // make the event draggable using jQuery UI
    $(this).draggable({
        zIndex: 999,
        revert: true,      // will cause the event to go back to its
        revertDuration: 0  //  original position after the drag
    });
});

And here is my drop function:

drop: function (date, jsEvent, ui, resourceId) {
    console.log('drop', date.format(), resourceId);
    $(this).parent().next().remove();
    $(this).remove();
    if ($('#external-events').find('.menu-event').length === 0) {
        $('#external-events').hide();
    }
},

Solution

  • @ADyson was right. From your reply to his comment above:

    The id also doesn't seem to show in the event object when it's on the calendar.

    In fact the external element IDs are lost before any dragging happens. Using console.log() in your $('#external-events .menu-event').each(...) iteration I found the inline data you specify in the html is completely overwritten by the new event data applied in JS.

    This is odd, because my undertsanding was that this should not happen. From the jQuery docs:

    In jQuery 1.4.3 setting an element's data object with .data(obj) extends the data previously stored with that element.

    (I assume that means v1.4.3 and later). After misunderstanding that line above all morning, I've realised the docs are saying you can extend the data object with new elements, eg:

    $('#selector').data('a', stuff);
    $('#selector').data('b', otherstuff);
    

    No problem, no conflict, the data on $('#selector') now includes both elements, and both values stuff and otherstuff.

    But that does not apply to trying to 'merge' an existing element with some new values, which is what you're doing:

    $('#selector').data('a', stuff);
    $('#selector').data('a', otherstuff);
    

    In this case, the end result is that data on a is otherstuff, not stuff merged with otherstuff. You are simply replacing the data value of a. This is obvious in hindsight, I think.

    The end result of this is that none of your external elements have an id when dropped on to the calendar. Calling removeEvents without specifying an id will remove all events.

    It seems the neatest option would be to specify your data all at once, in one place - all inline in the HTML, or all dynamically via JS. If you have to assign it from both, the solution is to properly merge the inline data with your new data as you iterate over the items:

    var elData = $(this).data('event'),
    newData = {
        title: $.trim($(this).text()),
        stick: true
    };
    $(this).data('event', $.extend(elData, newData));
    

    Note here that order is important, key:value pairs in newData will overwrite the matching key in elData.

    With this update I was able to get your code working. Here's a working JSFiddle.

    Side notes:

    • You are mixing use of jQuery and $, at least in your eventDragStop() callback.

    • Your JSON syntax for the inline data for event 2 and 3 is incorrect. JSON requires double quotes for its strings. To see this, I fixed the syntax of event 2 in the JSFiddle, and you can test that trashing event 1 and 2 (valid JSON) works. Try to trash event 3 (invalid JSON), and all events will be trashed.