Reputation: 1924
I have a sortable, connected draggables and also a droppable. It is basically a bucket and a sortable list of items. I want to limit the number of items you can drag from the bucket to the list.
My problem is that I got as far as reverting the draggable if the number of items in the list is to high. Also I remove the item from the list so it is not stuck there too. But here is my problem. The update event of the sortable fires last. So when the update event fires the element was removed already so there are less items than allowed. I don't want this as I update my database in the update event.
I know this question has been answered before but read the whole question first.
Here is the html:
<fieldset id="container">
<div style="float: left; width: 49%;">
<fieldset class="choosen" style="float: left; width: 100%;">
<legend>choosen</legend>
<div style="overflow-y: auto; height: 40em;" class="box">
<ul style="min-height: 40em;" class="imglist grey sortable">
<li data-id="1" class="acceptable">Element1</li>
<li data-id="2" class="acceptable">Element2</li>
<li data-id="3" class="acceptable">Element3</li>
</ul>
</div>
</fieldset>
</div>
<div style="float: left; margin-left: 2%; width: 49%;">
<fieldset class="bucket" style="float: left; width: 100%;">
<legend>bucket</legend>
<div style="overflow-y: auto; height: 40em;" class="box droppable">
<ul style="min-height: 40em;" class="imglist grey">
<li data-id="5" class="draggable"><a>Element5</a>
</li>
<li data-id="6" class="draggable"><a>Element6</a>
</li>
<li data-id="7" class="draggable"><a>Element7</a>
</li>
<li data-id="8" class="draggable"><a>Element8</a>
</li>
</ul>
</div>
</fieldset>
</div>
Here is the jquery code:
var helperClone = function (event, object) {
if (undefined == object) {
var helper = $(this).clone(),
height = $(this).height(),
width = $(this).width();
} else {
var helper = object.clone(),
height = object.height(),
width = object.width();
}
helper.css({'width': width, 'height': height});
return helper;
},
sortable = $(".sortable"),
draggable = $(".draggable"),
revertCheck = function () {
console.log("revertCheck", sortable.find("li").length > 4, sortable.find("li").length);
if (sortable.find("li").length > 4) {
return true;
}
return false;
};
draggable.draggable({
connectToSortable: sortable,
helper: helperClone,
revert: revertCheck
});
sortable.sortable({
placeholder: "ui-state-highlight",
helper: helperClone,
receive: function (event, ui) {
console.log("receive", $(this).find("li").length < 4, $(this).find("li").length);
if ($(this).find("li").length < 4) {
ui.item.remove();
$(this).data().uiSortable.currentItem.addClass('acceptable').css({
'width': '100%',
'height': 'unset'
});
} else {
ui.helper.remove();
}
},
update: function (event, ui) {
console.log("update", $(this).find("li").length < 4, $(this).find("li").length);
if ($(this).find("li").length < 4) {
//do stuff
console.log("update fired");
} else {
console.log("update didn't fire");
ui.item.effect('highlight', {
color: 'red'
});
}
}
});
$(".droppable").droppable({
accept: 'li.acceptable',
over: function (event, ui) {
$(this).effect('highlight');
},
drop: function (event, ui) {
var target = $(this).find('ul');
var clone = ui.draggable.clone().css({
'display': 'list-item',
'position': 'unset'
});
ui.draggable.remove();
clone.appendTo(target).show().removeClass('acceptable').draggable({
connectToSortable: sortable,
containment: "#container",
helper: helperClone,
revert: revertCheck
});
}
});
$("ul, li").disableSelection();
Here is a working example: http://jsfiddle.net/adq6exkp/
Other answers recommend calling sortable.sortable("cancel");
in the receive event of the sortable. So in the above code you would add this to the else part of receive event function:
$(ui.sender).sortable('cancel');
Problem is this raises the following error:
Error: cannot call methods on sortable prior to initialization; attempted to call method 'cancel' http://code.jquery.com/jquery-1.10.1.js Line 516
Here a working example for this error: http://jsfiddle.net/adq6exkp/1/
Upvotes: 2
Views: 1203
Reputation: 17124
You could handle the helper
removal in the update
event. That way number of elements in the list is consistent on both receive
and update
. But since you don't have access to the helper
in the update
event, you need to store it before. This can be done on receive
event. This way:
receive: function (event, ui) {
//store current helper
cur_helper = ui.helper;
console.log("receive", $(this).find("li").length < 4, $(this).find("li").length);
if ($(this).find("li").length < 4) {
ui.item.remove();
$(this).data().uiSortable.currentItem.addClass('acceptable').css({
'width': '100%',
'height': 'unset'
});
}
},
update: function (event, ui) {
console.log("update", $(this).find("li").length < 4, $(this).find("li").length);
if ($(this).find("li").length < 4) {
//do stuff
console.log("update fired");
} else {
//remove the helper if the list exceeds 3
cur_helper.remove();
console.log("update didn't fire");
ui.item.effect('highlight', {
color: 'red'
});
}
}
fiddle: http://jsfiddle.net/p0nft4mj/
Upvotes: 2
Reputation: 16116
If I understand, you don't want the update to fire if a revert took place.
You can accomplish this with a boolean.
sortable.sortable({
placeholder: "ui-state-highlight",
helper: helperClone,
receive: function (event, ui) {
console.log("receive", $(this).find("li").length < 4, $(this).find("li").length);
if ($(this).find("li").length < 4) {
ui.item.remove();
$(this).data().uiSortable.currentItem.addClass('acceptable').css({
'width': '100%',
'height': 'unset'
});
} else {
this.reverted = true;
ui.helper.remove();
}
},
update: function (event, ui) {
console.log("update", $(this).find("li").length < 4, $(this).find("li").length);
if (!this.reverted) {
//do stuff
console.log("update fired");
} else {
this.reverted = false;
console.log("update didn't fire");
ui.item.effect('highlight', {
color: 'red'
});
}
}
});
Upvotes: 1