Knockout Drag/Drop - Seating Chart
by Alvin Olavarrieta
HTML
<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<div id="roster" data-bind="foreach: students">
<div>
-
<span data-bind="drag: {value: student, disabled: $root.isLocked}" class="student">
<!-- ko if: student -->
<!-- ko with: student -->
<span data-bind="text:name, style:{backgroundColor: ((sex() == 1) ? 'lightBlue': 'pink')}"></span>
<span data-bind="visible: notes">(<span data-bind="text:notes"></span>)</span>
<!-- /ko -->
<!-- /ko -->
</span>
</div >
</div>
<input type="checkbox" data-bind="checked: isLocked" id="isLocked"/> <label for="isLocked">Locked</label>
<table id="floor">
<tbody data-bind="foreach: getRows()">
<tr class="row" data-bind="foreach: $data">
<td data-bind="drop: {value: student, disabled: !!student()}">
<div class="seat" >
<span data-bind="drag: {value: student, disabled: $root.isLocked}">
<!-- ko with: student -->
<span data-bind="" class="student">
<span data-bind="text:name, style:{backgroundColor: ((sex() == 1) ? 'lightBlue': 'pink')}"></span>
<br/>
<span data-bind="visible: notes">(<span data-bind="text:notes"></span>)</span>
</span>
<!-- /ko -->
</span>
</div>
</td>
</tr>
</tbody>
</table>
<a href="#" data-bind="click: function(){ console.log(ko.toJS(this)); }" style="clear:both;float:right">console.log the JSON</a>
CSS
*{
font-family: Helvetica, Arial;
}
#roster{
float:left;
}
.student{
margin:2px;
font-size:10pt;
}
.ui-dragon{
font-size:14pt;
}
#floor{
border:1px solid gray;
float:right;
}
.seat{
border:1px solid black;
margin:5px 10px 5px 10px;
width:80px;
height:80px;
}
JavaScript
(function($, ko) {
var _dragged, _hasBeenDropped, _draggedIndex;
ko.bindingHandlers.drag = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var dragElement = $(element);
var dragOptions = {
helper: function() {
return dragElement.clone().addClass("ui-dragon");
},
revert: true,
revertDuration: 0,
start: function() {
_hasBeenDropped = false;
_dragged = ko.utils.unwrapObservable(valueAccessor().value);
if ($.isFunction(valueAccessor().value)) {
valueAccessor().value(undefined);
dragElement.draggable("option", "revertDuration", 500);
} else if (valueAccessor().array) {
_draggedIndex = valueAccessor().array.indexOf(_dragged);
valueAccessor().array.splice(_draggedIndex, 1);
}
},
stop: function(e, ui) {
if (!_hasBeenDropped) {
if ($.isFunction(valueAccessor().value)) {
valueAccessor().value(_dragged);
} else if (valueAccessor().array) {
valueAccessor().array.splice(_draggedIndex, 0, _dragged);
}
}
},
cursor: 'default'
};
dragElement.draggable(dragOptions).disableSelection();
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
var dragElement = $(element);
var disabled = !! ko.utils.unwrapObservable(valueAccessor().disabled);
dragElement.draggable("option", "disabled", disabled);
}
};
ko.bindingHandlers.drop = {
init: function(element, valueAccessor,...