Part Ext : How to create sortable lists using Knockout JS

by corinnekm

HTML

<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.debug.js"></script>
<table class="fruitGrid">
        <thead>
            <tr>
                <th class="headRow headColumn">
                    Fruit Name
                </th>
                <th class="headRow headColumn">
                    Fruit Colour
                </th>
                <th class="headRow tools">
                </th>
            </tr>
        </thead>
        <tbody data-bind="foreach: fruits, sortable: fruits">
            <tr class="row">
                <td class="rowItem">
                    <label class="read" data-bind="text: name" />
                </td>
                <td class="rowItem">
                      <label class="read" data-bind="text: colour" />
                </td>
            </tr>
        </tbody>
    </table>

CSS

/* Grid */

.fruitGrid { 
    /* structure */
    width: 500px; 
    padding: 5px; 
}

.fruitGrid .headColumn { 
    /* structure */
    padding: 10px; 
    
    /* skin */
    color: White;
    background-color: #888;

}

.fruitGrid .row:nth-child(even) { 
    /* skin */
    background-color:#eee; 
}

.fruitGrid .rowItem { 
    /* structure */
    padding: 10px; 

}

.fruitGrid .rowItem .read   { 
    /* structure */
    display: block;
    width: 150px;
}

JavaScript

/*----------------------------------------------------------------------*/
/* Extended KO Bindings                                                 */
/*----------------------------------------------------------------------*/
ko.bindingHandlers.sortable = {
    init: function (element, valueAccessor) {
        // cached vars for sorting events
        var startIndex = -1,
            koArray = valueAccessor();
        
        var sortableSetup = {
            // cache the item index when the dragging starts
            start: function (event, ui) {
                startIndex = ui.item.index();
                
                // set the height of the placeholder when sorting
                ui.placeholder.height(ui.item.height());
            },
            // capture the item index at end of the dragging
            // then move the item
            stop: function (event, ui) {
                
                // get the new location item index
                var newIndex = ui.item.index();
                
                if (startIndex > -1) {
                    //  get the item to be moved
                    var item = koArray()[startIndex];
                     
                    //  remove the item
                    koArray.remove(item);
                    
                    //  insert the item back in to the list
                    koArray.splice(newIndex, 0, item);

                    //  ko rebinds the array so remove duplicate ui item
                    ui.item.remove();
                }

            },
            placeholder: 'fruitMoving'
        };
        
        // bind
        $(element).sortable( sortableSetup );  
    }
};

/*----------------------------------------------------------------------*/
/* View Model
/*----------------------------------------------------------------------*/
function FruitColourViewModel() {
    var self = this;
    
    //  data
    self.availableColours = [];
    self.fruits = ko.observableArray([]);
...