Sortable List
http://www.knockmeout.net/2011/05/dragging-dropping-and-sorting-with.html
by rniemeyer
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">
<a href="#" data-bind="text: name, click: function() { viewModel.selectTask($data); }, visible: $data !== viewModel.selectedTask()"></a>
<input data-bind="value: name, visibleAndSelect: $data === viewModel.selectedTask(), event: { blur: function() { viewModel.selectTask(''); } }" />
</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([
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")
]),
selectedTask: ko.observable(),
selectTask: function(task) {
this.selectedTask(task);
},
addTask: function() {
var task = new Task("new");
this.selectedTask(task);
this.tasks.push(task);
}
};
//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);
}
ui.item.remove();
}
});
}
};
//control visibility, give element focus, and select the contents (in order)
ko.bindingHandlers.visibleAndSelect = {
update: function(element, valueAccessor) {
ko.bindingHandlers.visible.update(element, valueAccessor);
if (valueAccessor()) {
setTimeout(function() {
$(element).focus().select();
}, 0); //new tasks are not in DOM yet
}
}
}
ko.applyBindings(viewModel);