Sortable List

http://www.knockmeout.net/2011/05/dragging-dropping-and-sorting-with.html

by kougiland

HTML

<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.debug.js"></script>
<div id="main">
    <h2>Tasks</h2>
    <div class="container" data-bind="template: { name: 'taskTmpl', foreach: tasks }, sortableList: tasks"></div>
    <a href="#" data-bind="click: addTask">Add Task</a>
    
    <script id="taskTmpl" type="text/html">
        <div class="item">
            <span data-bind="text: index"></span>
            <input data-bind="value: name" />
        </div>
    </script>
</div>    

<div id="results">
    <h2>Tasks</h2> 
    <ul data-bind="template: { name: 'resultTmpl', foreach: tasks }"></ul>
</div>

<script id="resultTmpl" type="text/html">
    <li data-bind="text: name"></li>
</script>

CSS

body { font-family: arial; }
h2 { font-weight: bold; }
div {  padding: 5px; margin: 5px; border: black 1px solid; }
p, a { font-size: .8em; }
ul { padding-bottom: 10px; }
li { padding: 2px; }
.container {  width: 125px; min-height: 50px; background-color: #AAA;}
.item { background-color: #DDD; cursor: move; }
.item input { width: 100px; }
#main { float: left; }
#results { margin-left: 175px; width: 150px; }

JavaScript

function Task(name) {
    this.name = ko.observable(name);
}
var viewModel = {
    tasks: ko.observableArray(),
    addTask: function() {
        var task = new Task("new");
        this.tasks.push(task);
    }
};

//attach index to items whenever array changes
viewModel.tasks.subscribe(function() {
    var tasks = this.tasks();
    for (var i = 0, j = tasks.length; i < j; i++) {
       var task = tasks[i];
        if (!task.index) {
           task.index = ko.observable(i);  
        } else {
           task.index(i);   
        }
    }
}, viewModel);

viewModel.tasks([
        new Task("Get dog food"),
        new Task("Mow lawn"),
        new Task("Fix car"),
        new Task("Fix fence"),
        new Task("Walk dog"),
        new Task("Read book")
        ]);

//connect items with observableArrays
ko.bindingHandlers.sortableList = {
    init: function(element, valueAccessor) {
        var list = valueAccessor();
        $(element).sortable({
            update: function(event, ui) {
                //retrieve our actual data item
                var item = ui.item.tmplItem().data;
                //figure out its new position
                var position = ko.utils.arrayIndexOf(ui.item.parent().children(), ui.item[0]);
                //remove the item and add it back in the right spot
                if (position >= 0) {
                    list.remove(item);
                    list.splice(position, 0, item);
                }
            }
        });
    }
};

ko.applyBindings(viewModel);