Reputation: 8054
Here I'm working on code that uses Knockout sortable. It should display nested sortables 3+ levels in depth.
The data are passed to the template, but some elements return to their original position after being dropped (e.g. A,B elements on uppermost level).
How should I configure the nested sortable so that the uppermost level can be also used without the error?
ERROR:
knockout-sortable.js:244 Uncaught TypeError: sourceParent.splice is not a function
or
knockout-sortable.js:252 Uncaught TypeError: targetParent.splice is not a function
JSFiddle - Similar working example
var viewModel = function() {
var self = this;
self.children = ko.observable([{
"name": "A",
"children": [{
"name": "A1",
"children": [{
"name": "A11"
}, {
"name": "A12"
}]
}, {
"name": "A2"
}]
}, {
"name": "B",
"children": [{
"name": "B1"
}, {
"name": "B2"
}]
}]);
}
ko.applyBindings(new viewModel());
ul {
border: solid 1px green;
list-style-type: none;
margin:0px;
}
li {
padding: 10px;
border: solid 1px blue;
margin:0px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://rawgithub.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<script id="nodeTmpl" type="text/html">
<li>
<!-- ko if: $data.name -->
<a href="#" data-bind="text: $data.name"></a>
<!-- /ko -->
<!-- ko if: $data.children -->
<ul data-bind="sortable: { template: 'nodeTmpl', data: $data.children }"></ul>
<!-- /ko -->
</li>
</script>
<ul data-bind="sortable: { template: 'nodeTmpl', data: $root.children }"></ul>
Upvotes: 0
Views: 882
Reputation: 2020
The solution provided by Peter Gerhat is incorrect. The problem is the fact that an observable of an array was used, rather than an observableArray.
The error states that sourceParent
(which is the collection on the viewmodel from which the element is being dragged), does not have a splice
function. Which is correct, an observable does not have a splice
function, while an array
or an observableArray
does. The same applies to targetParent
(which is the collection on the viewmodel to which the element is being dragged).
To make the initial sample work, all that needs to be done is change observable
to observableArray
.
However, as you will notice in the original sample and the solution provided by Peter Gerhat, even though no error is thrown, it still doesn't really work as it should. You'll notice that while you're dragging elements either don't move at all, or disappear.
The solution here is to turn each array
into an observableArray
, so that they can be updated by the sortable
-binding.
var viewModel = function() {
var self = this;
self.children = ko.observableArray([{
"name": "A",
"children": ko.observableArray([{
"name": "A1",
"children": ko.observableArray([{
"name": "A11"
}, {
"name": "A12"
}])
}, {
"name": "A2"
}])
}, {
"name": "B",
"children": ko.observableArray([{
"name": "B1"
}, {
"name": "B2"
}])
}]);
}
ko.applyBindings(new viewModel());
ul {
border: solid 1px green;
list-style-type: none;
margin: 0px;
}
li {
padding: 10px;
border: solid 1px blue;
margin: 0px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://rawgithub.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<script id="nodeTmpl" type="text/html">
<li>
<!-- ko if: $data.name -->
<a href="#" data-bind="text: $data.name"></a>
<!-- /ko -->
<!-- ko if: $data.children -->
<ul data-bind="sortable: { template: 'nodeTmpl', data: $data.children }"></ul>
<!-- /ko -->
</li>
</script>
<ul data-bind="sortable: { template: 'nodeTmpl', data: $root.children }"></ul>
Upvotes: 1
Reputation: 8054
The problem is solved by making changes to lines <ul data-bind="template: { name: 'nodeTmpl', data: $root }"></ul>
and I removed $data
from some references, because it was causing the error.
var viewModel = function() {
var self = this;
self.children = [{
"name": "A",
"children": [{
"name": "A1",
"children": [{
"name": "A11",
}, {
"name": "A12",
}]
}, {
"name": "A2",
}]
}, {
"name": "B",
"children": [{
"name": "B1",
}, {
"name": "B2",
}]
}]
};
ko.applyBindings(new viewModel());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<script src="https://rawgithub.com/rniemeyer/knockout-sortable/master/build/knockout-sortable.js"></script>
<script id="nodeTmpl" type="text/html">
<li>
<!-- ko if: $data.name -->
<a href="#" data-bind="text: name"></a>
<!-- /ko -->
<!-- ko if: $data.children -->
<ul data-bind="sortable: { template: 'nodeTmpl', data: children }"></ul>
<!-- /ko -->
</li>
</script>
<ul data-bind="template: { name: 'nodeTmpl', data: $root }"></ul>
Upvotes: -1