Reputation: 9358
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?
Upvotes: 9
Views: 3846
Reputation: 114792
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/
Upvotes: 23
Reputation: 8556
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>
Upvotes: 7