I'm trying to use KnockoutJS to build an interactive dashboard with data bindings on different aspects of the style
property; namely left
, top
, width
, and height
. For example, I'm using JQuery UI along with ui-draggable and ui-resizable effects to enable users to drag panels around on a canvas and resize them however they see fit. Without KnockoutJS, I was simply iterating over each div and stealing these properties from the dom element. I'm sure there's a better way using KnockoutJS, right?
To explain my problem a little better, consider two panels side by side:
<div id='dashboard'>
<div id='panel1' class='ui-draggable ui-resizable' data-bind='?????'>
<!-- content goes here -->
</div>
<div id='panel2' class='ui-draggable ui-resizable' data-bind='?????'>
<!-- content goes here -->
</div>
<button data-bind='click: send'>Update</button>
</div>
My view model looks something like this (note: pseudo coded for brevity):
var viewModel = {
panels: [
{ id: 'panel1', left: 0, top: 0, width: 50; height: 50 },
{ id: 'panel2', left: 50, top: 0, width: 50; height: 50 } ],
send: function() {
ko.utils.postJson( location.href , { panels: this.panels } );
}
};
ko.applyBindings( '#dashboard', viewModel );
I'd like to bind it in such a way that when the user sizes any of the div elements (ie. the panels), that it would then update the underlying viewModel using the built in data binding OnMouseUp
. Then when the user clicks the "Update" button, I would then post the coordinates to the server.
I'm thinking it might have something to do with custom templates, but even there I ran into some plexities that I couldn't quite grasp in managing the mouse events on each panel.
<script type='text/html' id='panelTemplate'>
<div class='ui-draggable ui-resizable'
style='top: ${ top }; left: ${ left }; width: ${ width }px; height: ${ height }px;'>
<!-- content goes here -->
</div>
</script>
Ideas?
I'm trying to use KnockoutJS to build an interactive dashboard with data bindings on different aspects of the style
property; namely left
, top
, width
, and height
. For example, I'm using JQuery UI along with ui-draggable and ui-resizable effects to enable users to drag panels around on a canvas and resize them however they see fit. Without KnockoutJS, I was simply iterating over each div and stealing these properties from the dom element. I'm sure there's a better way using KnockoutJS, right?
To explain my problem a little better, consider two panels side by side:
<div id='dashboard'>
<div id='panel1' class='ui-draggable ui-resizable' data-bind='?????'>
<!-- content goes here -->
</div>
<div id='panel2' class='ui-draggable ui-resizable' data-bind='?????'>
<!-- content goes here -->
</div>
<button data-bind='click: send'>Update</button>
</div>
My view model looks something like this (note: pseudo coded for brevity):
var viewModel = {
panels: [
{ id: 'panel1', left: 0, top: 0, width: 50; height: 50 },
{ id: 'panel2', left: 50, top: 0, width: 50; height: 50 } ],
send: function() {
ko.utils.postJson( location.href , { panels: this.panels } );
}
};
ko.applyBindings( '#dashboard', viewModel );
I'd like to bind it in such a way that when the user sizes any of the div elements (ie. the panels), that it would then update the underlying viewModel using the built in data binding OnMouseUp
. Then when the user clicks the "Update" button, I would then post the coordinates to the server.
I'm thinking it might have something to do with custom templates, but even there I ran into some plexities that I couldn't quite grasp in managing the mouse events on each panel.
<script type='text/html' id='panelTemplate'>
<div class='ui-draggable ui-resizable'
style='top: ${ top }; left: ${ left }; width: ${ width }px; height: ${ height }px;'>
<!-- content goes here -->
</div>
</script>
Ideas?
Share Improve this question edited Feb 20, 2014 at 23:35 Jeroen 63.9k47 gold badges228 silver badges366 bronze badges asked Jan 17, 2011 at 20:23 LucLuc 1,8702 gold badges23 silver badges38 bronze badges4 Answers
Reset to default 5With time passing the way that it does I was not able to see Green's solution in JSFiddle and the solution from Awais seemed a little long so I slugged through it some more.
I found the following binding works for setting the width. The parens and the 'px' seem to be important. In my case 'AWidth' is the width of a div tag.
data-bind="style: {width: AWidth() + 'px'}"
I was having exactly the same problem: bind a KnockoutJS model to collection of draggable and resizable divs. The solution is to use custom binding and handle the event of end of resize and end of drag, as I explain here.
$(document).ready(function () {
$('#btnSave').click(function () {
$.ajax({
url: '/Save.ashx',
contentType: "application/json; charset=utf-8",
type: 'POST',
dataType: 'json',
data: ko.toJSON(viewModel),
error: function () { alert("ajax error"); }
});
});
ko.bindingHandlers.jqDraggableResizable = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
var obj = valueAccessor();
var $elem = $(element);
element.dataObj = obj;
$elem.resizable({
stop: function (event, ui) {
this.dataObj.H(ui.size.height);
this.dataObj.W(ui.size.width);
}
});
$elem.draggable({
stop: function (event, ui) {
this.dataObj.X(ui.position.left);
this.dataObj.Y(ui.position.top);
}
});
}
}
ko.applyBindings(viewModel);
});
This question was answered over on the KnockoutJS Google Group by a brilliant developer named Green.
He demonstrated how to do this using a custom extension library (that he built) for KnockoutJS. His solution allow additional JQueryUI events to be "bindable" (ie. drag & resize). He then posted his solution along with his knockout-ext.js file out on JSFiddle for me to use and learn from it.
Thanks again, Green! :)
I was able to get it working with a simple solution, it builds off Ryans view.
This allows me to save my changes real time as I drag drop elements, with BreezeJS
in viewmodel
ko.bindingHandlers.draggable = {
init: bindDraggable
};
function bindDraggable(element, value) {
$(element).draggable({
stop: onStopDrag
});
function onStopDrag(event, ui) {
ko.dataFor(element).PositionX(ui.position.left);
ko.dataFor(element).PositionY(ui.position.top);
datacontext.saveChanges(); //if you want real time updating on drop
}
}
and in my view
<div data-bind="foreach: posts">
<div class="BoardPost" data-bind="style: { top: PositionY() + 'px', left: PositionX() + 'px' }, draggable:null">
<div class="Title" data-bind="text: Content"></div>
</div>