When I'm trying to delete an external event from my calendar, if I add say 3 external events then drag one to the bin, rather than removing just the one event it deletes all events (even the ones from the separate feeds I am doing.
Any idea why this is and how to fix it? Here is the code:
$(document).ready(function () {
//listens for drop event
$("#calendarTrash").droppable({
tolerance: 'pointer',
drop: function (event, ui) {
var answer = confirm("Delete Event?")
if (answer) {
$('#calendar').fullCalendar('removeEvents', event.id);
}
}
});
/* initialize the external events ------------*/
$('#external-events div.external-event').each(function () {
// create an Event Object (http://arshaw.com/fullcalendar/docs/event_data/Event_Object/)
// it doesn't need to have a start or end
var eventObject = {
title: $.trim($(this).text()) // use the element's text as the event title
};
// store the Event Object in the DOM element so we can get to it later
$(this).data('eventObject', eventObject);
// 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
});
});
});
The event.id in your drop function does not refer to the FullCalendar event. It refers to the drop event that was just triggered.
You will need to use ui.draggable to access your draggable - in this case the FullCalendar event.
Hope this helps! Cool concept BTW!
Update: Check this fiddle for a proof-of-concept
For anyone in the same circumstances,..
eventDragStop: function(event, jsEvent, ui, view) {
if (x) {
$('#calendar').fullCalendar('removeEvents', event._id);
}
Please notice I am using event._id, x is the result of checking which div the item is dragged into returning a true or false. checks which div the event is being dropped into. I also had to change some code in fullcalendar.js
the function eachEventElement, was causing me an issue with the above code, so I changed it too.
function eachEventElement(event, exceptElement, funcName) {
try{
var elements = eventElementsByID[event._id],
i, len = elements.length;
for (i=0; i<len; i++) {
if (!exceptElement || elements[i][0] != exceptElement[0]) {
elements[i][funcName]();
}
}
}
catch(err)
{}
}
Related
Is there any known method how to trigger the drop event from React via jQuery?
There seems to be little documentation about this topic, in general you should avoid using both but lets assume in this case it just makes sense.
I tried so far this, but the event seems to not receive on reactside.
$(document).ready(function() {
$(".draggable").draggable({
revert: "invalid"
});
$(".droppable").droppable({
accept: ".draggable",
classes: {
"ui-droppable-active": "ui-state-active",
"ui-droppable-hover": "ui-state-hover"
},
drop: function(event, ui) {
var event = document.createEvent("HTMLEvents");
event.initEvent("drop", true, true);
$(this)[0].dispatchEvent(event);
console.log("works as designed");
}
});
});
I have just finished implementing jQuery UI's drag-n-drop functionality, with .draggable() and .droppable(), in React. As you pointed out, you wouldn't normally want to do this, but sometimes it's the best tool for the job.
Your question doesn't entirely make sense to me. The drop event triggers when the .draggable() jQuery-managed element is dropped onto the .droppable() jQuery-managed element. So when you say that you want to "trigger the drop event from reactjs via jQuery", do you mean that you want to call the same function that jQuery uses when it drops the item, but you want to call that event from within React?? Or are you just trying to say that you want to use jQuery's drag-n-drop functions inside React?? Because those aren't the same thing. I'm going to assume that you're just trying to call jQuery's drag-n-drop features from within React.
import $ from 'jquery';
import 'jquery-ui/ui/widgets/draggable';
import 'jquery-ui/ui/widgets/droppable';
class SomeComponent extends React.Component {
setDroppable() {
const reactComponent = this;
$('.roleListItems').droppable({
drop : function(event, ui) {
const roleId = event.target.getAttribute('roleid');
const userId = ui.draggable[0].getAttribute('userid');
if (!reactComponent.roleUserExists(roleId, userId)) { session.ListOfRolesCarousel.callCreateRoleUser(roleId, userId); }
else { reactComponent.setSelectedRoleId(roleId, true); }
},
tolerance : 'touch',
});
}
setDraggable() {
const grabbingCursor = this.grabbingCursor;
$('.userListItems').draggable({
appendTo : document.getElementById('rolesContainer'),
helper : 'clone',
revert : 'invalid',
snap : '.roleListItems',
snapMode : 'inner',
snapTolerance : 25,
start : function(event, ui) {
ui.helper[0].style.cursor = grabbingCursor;
ui.helper[0].style.width = $('#rolesContainer').width() + 'px';
},
});
}
}
Here I'm:
Importing jQuery, as well as the draggable and droppable widgets
Inside .droppable(), I'm grabbing the values that I need from the dropped element (in this case, it's the roleId and the userId)
Then I call React functions/methods to process those values
Notice that I had to create a variable called reactComponent and set it to this so I could call the React method from inside the drop() function. This is necessary because this has a different meaning once you're inside jQuery's drop() method.
My fullcalendar duplicates events visually when i drag them to another timeslot. I have simplified my code down to the eventDrop to isolate the issue and yet I'm unable to understand the issue.
If I store the events to my localStorage I don't get a duplicate in the storage and the duplicate disappears when I reload the page. This means the problem is only visual and with Full Calendar itself.
However, this is obviously a huge issue as I don't want to reload the page: I want to stay in the current view changing what I need.
Here's my code for the eventDrop:
eventDrop: function(event, delta, revertFunc, jsEvent, ui, view) {
if (!confirm("Are you sure you want to change " + event.title + " ?")) {
/*If the user cancels the change, the event returns to its original position. Otherwise it saves the event.*/
revertFunc(); /*Revert changes using the predefined function revertFunc of fullCalendar.*/
$("#calendar").fullCalendar("refetchEvents");
/*FullCalendar Method to refetch events from all sources and rerender them on the screen.*/
} else {
updateConfirm(event); /*Use the logic to confirm the event update properties*/
Evento.prototype.Update(event); /*Modify the targeted event on the localStorage using the Update method of the Evento Class*/
$("#calendar").fullCalendar("updateEvent", event);
/*FullCalendar Method to report changes to an event and render them on the calendar.*/
$("#calendar").fullCalendar("refetchEvents");
/*FullCalendar Method to refetch events from all sources and rerender them on the screen.*/
}
}
And here's a gif of the issue:
https://i.imgur.com/rFPvvjE.gif
UPDATE: With slicedtoad's help I isolated the issue to my updateConfirm logic:
var updateConfirm = function(event) {
if (confirm("New title?")) { /*Check if the user wants to change the event title*/
event.title = prompt("Enter the new title: "); /*Set new title for the event*/
} else {
event.title = event.title;
}
if (confirm("New description?")) { /*Check if the user wants to change the event description*/
event.description = prompt("Enter the new description: "); /*Set new description for the event*/
} else {
event.description = event.description;
}
if (confirm("Is the event important?")) { /*Check if the user wants to change the event priority*/
event.overlap = false;
event.backgroundColor = "red"; /*Set new priority for the event*/
} else {
event.overlap = true;
event.backgroundColor = "blue"; /*Set new priority for the event*/
}
};
UPDATE 2:
console.log(event) before updateConfirm(event):
Object {id: "2015-01-27T15:29:11+00:00", title: "título", start: m, end: m, allDay: false…}_allDay: false_end: m_id: "2015-01-27T15:29:11+00:00"_start: mallDay: falsebackgroundColor: "blue"className: Array[0]description: "gt4"end: mid: "2015-01-27T15:29:11+00:00"overlap: truesource: Objectstart: mstoringId: "2015-01-27T15:29:11+00:00"title: "título"__proto__: Object
console.log(event) after updateConfirm(event):
Object {id: "2015-01-27T15:29:11+00:00", title: "título", start: m, end: m, allDay: false…}_allDay: false_end: m_id: "2015-01-27T15:29:11+00:00"_start: mallDay: falsebackgroundColor: "blue"className: Array[0]description: "gt4"end: mid: "2015-01-27T15:29:11+00:00"overlap: truesource: Objectstart: mstoringId: "2015-01-27T15:29:11+00:00"title: "título"__proto__: Object
Since the event is not locally sourced, calling updateEvent isn't necessary since the event will be refetched from the database when you call $("#calendar").fullCalendar("refetchEvents");
I'm not entirely sure why it would duplicate but the event modified by updateEvent seems to persist past the refetch. You must be changing it's ID or replacing it with another event object, but I wasn't able to reproduce it.
So try removing the update line
} else {
updateConfirm(event);
Evento.prototype.Update(event);
$("#calendar").fullCalendar("refetchEvents");
}
If that doesn't work, try deleting the event manually:
$("#calendar").fullCalendar( 'removeEvents', event._id ) //replace the updateEvent call with this
//or event.id if your events have an explicit id
Addendum
You likely want to actually figure out the cause of the problem since the above just patches it. Something in Evento.prototype.Update updateConfirm is modifying the event to the point that FC thinks it is a different event. Is it being copied and replacing itself? Are you playing with it's id?
Do singleton style solution:
This worked for me to stop the duplication which was caused by "new Draggable(container)"
every time reloaded the view
$scope.Draggable = null if ($scope.Draggable == null) {
$scope.Draggable = new Draggable(containerEl, {
itemSelector: '.item',
eventData: function (eventEl) {
return {
title: eventEl.innerText
};
}
});
}
I would like to use a method of an dragged and dropped Object.
function Controller(){
$( "#container" ).droppable({
drop: function( event, ui ) {
var draggable = ui.draggable;
alert( "Dropped:" + draggable.attr('id'));
draggable.myMethod();
}
});
Could you explain why this doesnt work?
The alert shows that the right Object ist dropped,
but i cant use the method.
The Object looks like this:
function Icon(name) {
var name = name;
this.myMethod = function() {
alert("test")};
this.getInfo = function() {
var dataname = this.getName();
//BErtram
$("#" + dataname).draggable({ revert: "valid" });
//Bertram Ende
}
}
Edit:
If I invoke the Method this way:
drop: function( event, ui ) {
var draggable = ui.draggable;
alert(...);
var ic = new Icon("asdsa");
ic.myMethod();
},
it works, but I want to use the Method on the dragged Object, do I have to do some sort of typecasting?
Edit: Workaround
Finally i used a workaround, using a bool to check if there was a succesfull drop and invoking the method in draggable.stop if the boolean ist true. The state of the boolean is set to true in the droppable on a succesfull drop and set to false in draggable.start.
The problem is that myMethod is a function on the Icon object you create, however the ui.draggable is not an instance of Icon - so it has no myMethod method.
The ui.draggable value is a DOM element contained inside a jQuery object. You need a mechanism to map from the element back to the Icon that's responsible for it.
The simplest method is to use .data():
function Icon(name) {
$('#' + name).data('self', this);
...
}
and then retrieve that object in the event handler:
drop: function(event, ui) {
var draggable = ui.draggable;
var icon = draggable.data('self');
icon.myMethod();
}
Problem (jsFiddle demo of the problem)
I'm having some trouble with the revert setting when used in conjunction with the cancel method in the jQuery sortable. The cancel method, as documented in the jQuery Sortable documentation states:
Cancels a change in the current sortable and reverts it back to how it
was before the current sort started. Useful in the stop and receive
callback functions.
This works fine in both the stop and receive callbacks, however if I add a revert duration to the sortable connected list, it starts to act funny (see jsFiddle here).
Ideally, upon cancelling, the revert could simply not happen, or alternatively in a more ideal world, it would gracefully revert to it's original location. Any ideas how I can get the revert and cancel to play nice?
Expected
Drag from left list to right list
Drop item
Item animates to original location - or - immediately shifts to original location
Actual
Drag from left list to right list
Drop item
Item animates to new location, assuming sortable is successful
Item immediately shifts to original location, as sortable was cancelled
Clarification
The revert property moves the item to the location where the item would drop if successful, and then immediately shifts back to the original location due to the revert occurring before the cancel method. Is there a way to alter the life-cycle so if the cancel method is executed, revert isn't, and instead the item is immediately return to it's original location?
i created a demo for you here:
the jsfiddle code
it seems to produce the output you expect.
i changed the receive callback method from this:
$(ui.sender).sortable('cancel');
to this:
$(ui.sender).sortable( "option", "revert", false );
hopefully, this is what you expected.
After many hours for searching for a solution I decided the only way to achieve what I was trying to do was to amend the way in which the jQuery sortable plugin registered the revert time. The aim was to allow for the revert property to not only accept a boolean or integer, but also accept a function. This was achieved by hooking into the prototype on the ui.sortable with quite a lot of ease, and looks something like this.
jQuery Sortable Hotfix
$.ui.sortable.prototype._mouseStop = function(event, noPropagation)
{
if (!event) return;
// if we are using droppables, inform the manager about the drop
if ($.ui.ddmanager && !this.options.dropBehaviour)
$.ui.ddmanager.drop(this, event);
if (this.options.revert)
{
var self = this;
var cur = self.placeholder.offset();
// the dur[ation] will not determine how long the revert animation is
var dur = $.isFunction(this.options.revert) ? this.options.revert.apply(this.element[0], [event, self._uiHash(this)]) : this.options.revert;
self.reverting = true;
$(this.helper).animate({
left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
}, !isNaN(dur) ? dur : 500, function ()
{
self._clear(event);
});
} else
{
this._clear(event, noPropagation);
}
return false;
}
Implementation
$('ul').sortable({
revert: function(ev, ui)
{
// do something here?
return 10;
}
});
I ended up creating a new event called beforeRevert which should return true or false. If false then the cancel function is called and the item is animated back to its original position. I didn't code this with the helper option in mind, so it would probably need some additional work to support that.
jQuery Sortable Hotfix with animation
var _mouseStop = $.ui.sortable.prototype._mouseStop;
$.ui.sortable.prototype._mouseStop = function(event, noPropagation) {
var options = this.options;
var $item = $(this.currentItem);
var el = this.element[0];
var ui = this._uiHash(this);
var current = $item.css(['top', 'left', 'position', 'width', 'height']);
var cancel = $.isFunction(options.beforeRevert) && !options.beforeRevert.call(el, event, ui);
if (cancel) {
this.cancel();
$item.css(current);
$item.animate(this.originalPosition, {
duration: isNaN(options.revert) ? 500 : options.revert,
always: function() {
$('body').css('cursor', '');
$item.css({position: '', top: '', left: '', width: '', height: '', 'z-index': ''});
if ($.isFunction(options.update)) {
options.update.call(el, event, ui);
}
}
});
}
return !cancel && _mouseStop.call(this, event, noPropagation);
};
Implementation
$('ul').sortable({
revert: true,
beforeRevert: function(e, ui) {
return $(ui.item).hasClass('someClass');
}
});
I'm using JQuery UI - Selectable. I want to deselect the element if it has been pressed and it has already been selected (toggle)
Could you help me to add this functionality please !
Because of all the class callbacks, you're not going to do much better than this:
$(function () {
$("#selectable").selectable({
selected: function (event, ui) {
if ($(ui.selected).hasClass('click-selected')) {
$(ui.selected).removeClass('ui-selected click-selected');
} else {
$(ui.selected).addClass('click-selected');
}
},
unselected: function (event, ui) {
$(ui.unselected).removeClass('click-selected');
}
});
});
You already have that functionality with jQuery UI selectable if you're holding down the Ctrl key while clicking.
If you really need that as the default functionality, you don't need to use selectable, you can do it just as a simple onclick handler, like what nolabel suggested.
Or... you might as well edit jquery.ui.selectable.js and add an option which does just what you need. Shouldn't be too hard, there are 4 places where event.metaKey is checked, make sure if your option is set, just run the codepaths as if event.metaKey was always true.
As the selectables could use some more customisation, you could also make a feature request for jQuery UI developers to include it in the next official version.
You have to inject two simple elements in the code to achieve what you want. This just inverts the metaKey boolean, whenever you need inverted/toggle functionality.
options: {
appendTo: 'body',
autoRefresh: true,
distance: 0,
filter: '*',
tolerance: 'touch',
inverted: false /* <= FIRST*/
},
_mouseStart: function(event) {
var self = this;
this.opos = [event.pageX, event.pageY];
if (this.options.disabled)
return;
var options = this.options;
if (options.inverted) /* <= SECOND*/
event.metaKey = !event.metaKey; /* <= SECOND*/