I'm trying to make a list item draggable in a project that is using Knockout.js.
I'm using the code below, and as you can see it is very simple:
<div id="room-view" >
<ul data-bind="foreach: rooms">
<li data-bind="text: name" class="draggable room-shape"></li>
</ul>
</div>
<script type="text/javascript">
$(function() {
$( ".draggable" ).draggable();
});
</script>
The list of 'room' renders just fine, but none of the items are draggable. However, if I apply the 'draggable' class to any other element on the page - it becomes draggable. Even if they are other list items. The only difference is that these list items are being created through the 'foreach' binding.
Can anyone spot what may be causing this to not function correctly?
Definition of knockout (Entry 1 of 3) 1a : the act of knocking out : the condition of being knocked out. b(1) : the termination of a boxing match when one boxer has been knocked down and is unable to rise and resume boxing within a specified time. (2) : technical knockout. c : a blow that knocks out an opponent.
If a fighter loses consciousness ("goes limp") as a result of legal strikes, it is declared a KO. Even if the fighter loses consciousness for a brief moment and wakes up again to continue to fight, the fight is stopped and a KO is declared.
If you describe someone or something as a knockout, you think that they are extremely attractive or impressive. [informal, approval] She was a knockout in navy and scarlet. [
This word is also used to mean "gorgeous person," so if someone tells you you're a knockout, you can be sure they mean it as a compliment.
foreach
works by saving off a copy of the elements to use as the "template" whenever it needs to render an item. So, the elements that you made draggable are not the ones that were rendered by foreach
.
You could try to make sure that draggable
is called after applyBindings
, but that would only be effective if your rooms
is not an observableArray that changes. Any new items rendered would not be draggable.
Another option is to use the afterRender option to call draggable
on your elements.
A better way is to use a custom binding. It could be as simple as:
ko.bindingHandlers.draggable = {
init: function(element) {
$(element).draggable();
}
};
or you can get into something a little better where you actually update an observableArray based on where your items are dropped.
I wrote an article a while back on it here. With Knockout 2.0, I made a few changes to simplify the binding so that you can just use sortableList
on the parent.
Here is a sample with just sortable: http://jsfiddle.net/rniemeyer/DVRVQ/
Here is a sample with dropping between lists: http://jsfiddle.net/rniemeyer/sBHaP/
The problem is that draggable()
is applied when document is ready on existing elements. Knockout.js will modify the HTML and create new elements at the beginning and also when the rooms
array is updated.
What you need is to enable dragging each time the room-view
is rendered. You can use afterRender
for that.
Try this:
<div id="room-view" >
<ul data-bind="foreach: { data: rooms, afterRender: afterRender }">
<li data-bind="text: name" class="draggable room-shape"></li>
</ul>
</div>
<script type="text/javascript">
// this is your already existing view-model that contains rooms, etc
function roomsViewModel() {
// ...
// insert this in your view-model
this.afterRender = function() {
$( ".draggable" ).draggable();
}
}
ko.applyBindings(new roomsViewModel());
</script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With