KnockoutJS Sortable - How to get target data on afterMove arg (single observableArray)

1k Views Asked by At

html:

<div class="grid" data-bind="sortable: { data: tasks, afterMove: sortingTasks }">
    // ...
</div>

viewmodel:

//...

tasks = ko.observableArray([
  {
     id       : 1,
     name     : "tasks1",
     priority : 1
  },
  { 
     id       : 2,
     name     : "tasks2",
     priority : 2
  }
]);

sortingTasks = function(arg)
{
    var currentIndex = (arg.sourceIndex+1);
    var targetIndex  = (arg.targetIndex+1);

    // here the problem... 
}

What I want to do is:

  • When tasks2 is moved to the place of tasks1, so tasks2 priority will become 1 and tasks1 priority will become 2 (interchanges).

What I have try:

I have read documentation here: https://github.com/rniemeyer/knockout-sortable

There have a few object received when afterMove function is execute such as:

  • arg.item - the actual item being moved
  • arg.sourceParent - the original observableArray
  • arg.targetParent - the destination observableArray

The problem is:

Currently I use sorting for a single observableArray only.

So, I can use arg.item to change source priority with targetIndex:

sortingTasks = function(arg)
{
    var currentIndex = (arg.sourceIndex+1);
    var targetIndex  = (arg.targetIndex+1);

    // here the problem... 

    arg.item.priority = targetIndex; // change source item priority
}

but, how can i change the target item priority ?

the arg.sourceParent and arg.targetParent right now return the same original observableArray, so i can't get the target item to change it priority.

I wish i could have like arg.targetItem, so i can change the target item priority like this:

sortingTasks = function(arg)
{
    var currentIndex = (arg.sourceIndex+1);
    var targetIndex  = (arg.targetIndex+1);

    // here the problem... 

    arg.item.priority = targetIndex; // change source item priority
    arg.targetItem.priority = currentIndex; // change target item priority
}

EDIT: here is the http://jsfiddle.net/L7BBz/

2

There are 2 best solutions below

0
On BEST ANSWER

I've faced similar issue before. This is my POC you can refer to, this solves nested level task drag and drop and priority as well.

This POC obviously needs some tweaking and re-factoring but I am sure this is best for you to get started.

http://jsfiddle.net/rahulrulez/UUMqz/

I have used withIndex observable subscribe function written by Ryan Neimeyer..

ko.observableArray.fn.withIndex = function(prop) {
    //by default use an "index" observable
    prop = prop || "index";

    //whenever the array changes, make a single pass through to update the indexes
    this.subscribe(function(newValue) {
        var item;
        for (var i = 0, j = newValue.length; i < j; i++) {
            //create the observable if it does not exist
            item = newValue[i];

            if (!item[prop]) {
                item[prop] = ko.observable();
            }

            item[prop](i);   
        }
    }, this);

    return this;
};

This reiterates through the indexes / priorities of tasks and reset them. I found this the best solution so far..

0
On

This is a refactor of the code Rahul posted. I renamed some variables for clarity, added some comments, and removed some unused parameters.

ko.observableArray.fn.withIndex = function (propertyName) {
    //by default use an "index" as our observable property. 
    propertyName = propertyName || "index";

    //whenever the array changes, make a single pass through to update the indexes
    this.subscribe(updatedArray => {
        var currentItem;

        for (var i = 0, upperBound = updatedArray.length; i < upperBound; i++) {
            currentItem = updatedArray[i]; //get the item

            //create the observable if it does not exist
            if (!currentItem[propertyName]) {
                currentItem[propertyName] = ko.observable();
            }

            currentItem[propertyName](i); //update the sequence
        }
    }, this);

    return this;
};

Hope someone finds that useful. The original took me a minute to figure out, I can tell that a very smart person blasted that out as quickly as possible.