KO Sortable Binding v4
by whelkaholism
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.2/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.selection/1.0.1/jquery.selection.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons&_.css">
<button data-bind="click: add">
Add
</button>
<hr/>
<div class="car-park" data-bind="sortable: { multiColumn: true, boundField: CarPark, columnsField: 'Lanes', itemsField: 'Vehicles' }, foreach: CarPark().Lanes()">
<div class="lane" data-bind="foreach: Vehicles()">
<div class="vehicle"><i></i><span data-bind="text: ID"></span></div>
</div>
</div>
<xmp data-bind="click: purge, html: JSON.stringify(ko.toJS(CarPark()), null, '\t')"></xmp>
CSS
.lane {
width: 60px;
min-height: 60px;
padding-right: 20px;
float: left;
border-right: 1px dotted #000000;
}
.vehicle {
margin: 10px;
width: 40px;
height: 40px;
color: #000000;
cursor: pointer;
border: 1px solid transparent;
}
.vehicle:hover {
color: #00aa00;
cursor: move;
}
.vehicle span {
font-family: Arial;
font-weight: bold;
background: #000000;
color: #ffffff;
padding: 1px 3px;
font-size: 10px;
}
.vehicle i:before {
font-family: Material Icons;
content: 'directions_car';
width: 40px;
height: 40px;
font-size: 40px;
font-style: normal;
}
.sortable-placeholder {
border: 1px dotted black;
margin: 10px;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.5);
}
xmp {
float: right;
font-size: 10px;
font-weight: bold;
background: #ffffff;
padding: 4px;
margin-right: 50px;
}
JavaScript
var __i = 0;
function Model(data, mapping)
{
var _this = this;
var _vid = 7;
this.CarPark = ko.observable();
this.add = function()
{
_this.CarPark().Lanes()[Math.floor(Math.random() * 3)].Vehicles.push({ ID: _vid++ });
};
ko.mapping.fromJS({
CarPark: {
Lanes: [
{
ID: 'A',
Vehicles: [
{ ID: 1 },
{ ID: 2 },
]
},
{
ID: 'B',
Vehicles: [
{ ID: 3 }
]
},
{
ID: 'C',
Vehicles: [
{ ID: 4 },
{ ID: 5 },
{ ID: 6 }
]
},
]
},
}, {}, _this);
this.purge = function()
{
_this.CarPark.valueHasMutated();
_this.CarPark().Lanes.valueHasMutated();
};
}
ko.bindingHandlers.sortable = {
update:
function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
{
var val = ko.unwrap(f_valueaccessor());
var obs = ko.isObservable(f_valueaccessor()) ? f_valueaccessor() : null;
var multicolumn = false;
var placeholder = 'sortable-placeholder';
var colsfield = null;
var itemsfield = null;
var options = {};
if (typeof (val) == 'object')
{
multicolumn = val.multiColumn;
obs = val.boundField;
placeholder = typeof (val.placeholder) != 'undefined' ? val.placeholder : placeholder;
options = val.options;
colsfield = val.columnsField;
itemsfield = val.itemsField;
val = true;
}
obs && obs(); // Force KO to track this as a dependency
var obscolumns = obs && colsfield ?...