I have recently used a great stack overflow answer by RP Niemeyer, KnockoutJS ObservableArray data grouping, to allow me to group data in an observableArray by a field. That is working brilliantly. The problem is it is not playing nicely with Knockout Sortable. There is a problem with retrieving the sourceParent. Please see the following fiddle: http://jsfiddle.net/mrfunnel/g6rbc
Basically I have one nested list where tasks are grouped into routes (unscheduled) and another list of just tasks (scheduled). I want to be able to drag routes and tasks into scheduled. If a route is dragged in it needs to split up into tasks.
Any assistance would be greatly appreciated.
The sortable
binding only works against observableArrays, because it needs to know how to write the dropped value back to the array. With the result of a computed observable, it would not be able to write it in a meaningful way.
Here is an alternative way that you might be able to structure your code. Basically, you would build an observableArray of routes that each contain an observableArray of tasks. Something like:
self.tasks.subscribe(function(tasks) {
var routes = [],
routeIndex = {};
ko.utils.arrayForEach(tasks || [], function(task) {
var routeId = task.routeId(),
routeTasks = routeIndex[routeId];
//first time that we have seen this routeID
if (!routeTasks) {
//add it to the index, so we can find it without looping next time
routeIndex[routeId] = routeTasks = { id: routeId, tasks: ko.observableArray() };
//add it to the array that we will eventually return
routes.push(routeTasks);
}
routeTasks.tasks.push(task);
});
//return an array of routes that each contain an array of tasks
self.tasksByRoute(routes);
});
Then, you can use the beforeMove
callback on your scheduled tasks to check if it is a route rather than an individual task and do the splitting like:
self.myDropCallback = function(arg) {
var spliceArgs;
//determine if this is really a route rather than an individual task
if (arg.item && arg.item.tasks) {
//we will handle the drop ourselves, since we have to split into tasks
arg.cancelDrop = true;
//build up args, since first two need to be new index and items to remove
spliceArgs = [arg.targetIndex, null];
//add the tasks to the args
spliceArgs.push.apply(spliceArgs, arg.item.tasks());
//splice in the tasks at the right index
arg.targetParent.splice.apply(arg.targetParent, spliceArgs);
//remove the originals, after cancel has happened
setTimeout(function() {
arg.sourceParent.remove(arg.item);
}, 0);
}
};
Here is an updated sample: http://jsfiddle.net/rniemeyer/BeZ2c/. I am not sure if you allow sorting between the routes, but I disabled that in the sample. You can drop individual tasks or entire routes into the scheduled tasks.