I am making a editable tree structure with Knockout sortable and found this great example: http://jsfiddle.net/rniemeyer/Lqttf
That works fine, but I have a list of root nodes so I changed the root node binding to be a sortable list of tree items too. Fiddle: http://jsfiddle.net/yyqnhngm
The new template markup looks like this (notice that the root ul is a sortable
binding and not a template
as in the original):
<script id="nodeTmpl" type="text/html">
<li>
<a href="#" data-bind="text: name"></a>
<div>
<ul data-bind="sortable: { template: 'nodeTmpl', data: $data.children }"></ul>
</div>
</li>
</script>
<ul data-bind="sortable: { template: 'nodeTmpl', data: root }"></ul>
If you drag B into A, then B will be copied instead of moved into A. That's the issue I'm looking for a cause of and solution to. My immediate thought is that sortable thinks the item is dragged into both the lists at the same time, maybe because of a markup/html issue, but I can't see how.
Note: I know I could just wrap all the items into a root note, but that doesn't make much sense for my purposes.
It seems to me that there's no support for "ordinary" arrays. Changing the array in your viewmodel to an observableArray
gives you the required bahavior. I.e.:
ko.applyBindings({
root: ko.observableArray([
new TreeItem("A", []),
new TreeItem("B", [])
])
});
For reference:
According to the knockout-sortable readme (emphasis mine):
knockout-sortable is a binding for Knockout.js designed to connect observableArrays with jQuery UI's sortable functionality.
I also tried to go through the source to be able to explain what exactly was going wrong, but couldn't find the exact cause. I did find a snippet which seems to at least explain the author's thoughts.
The code shows that the plugin's default behavior uses the splice
method to move an element between arrays. This could explain why your example didn't trigger an exception: both Array.prototype
and a ko.observableArray
have a splice
method; both take similar arguments.
The last comment in the code block explains that targetParent
and sourceParent
are expected to be observable.
if (!sortable.hasOwnProperty("strategyMove") || sortable.strategyMove === false) {
//do the actual move
if (targetIndex >= 0) {
if (sourceParent) {
sourceParent.splice(sourceIndex, 1);
//if using deferred updates plugin, force updates
if (ko.processAllDeferredBindingUpdates) {
ko.processAllDeferredBindingUpdates();
}
}
targetParent.splice(targetIndex, 0, item);
}
//rendering is handled by manipulating the observableArray; ignore dropped element
dataSet(el, ITEMKEY, null);
}
Source: https://github.com/rniemeyer/knockout-sortable/blob/master/src/knockout-sortable.js#L250