-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The knockout code is not using the right paradigm #465
Comments
Can anyone confirm that this is working? For some reason, when i tried it, the grid stack wasn't getting a height - I see Thanks! Edit: Sorry, there's no issue with the height getting 0px initially. The issue was the addWidget wasn't getting called when the view model is updated. Adding the |
Good point. Thank you. I will take a look on it |
100% agree ko.bindingHandlers.gridStack = {
helpers: {
cloneNodes: function (nodesArray, shouldCleanNodes) {
for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
var clonedNode = nodesArray[i].cloneNode(true);
newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
}
return newNodesArray;
}
},
init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var $element = $(element);
var gridItems = [];
var fromObs = false;
var template = ko.bindingHandlers.gridStack.helpers.cloneNodes(element.getElementsByClassName('grid-stack-item'), true);
ko.virtualElements.emptyNode(element);
var timeout;
var grid = $element.gridstack(ko.utils.extend(ko.unwrap(valueAccessor().settings) || {}, {
auto: true
})).data('gridstack');
$element.on('change', function (eve, items) {
if (!fromObs) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function () {
for (var i = 0; i < gridItems.length; i++) {
var item = gridItems[i];
var from = {
x: ko.unwrap(item.item.x),
y: ko.unwrap(item.item.y),
width: ko.unwrap(item.item.width),
height: ko.unwrap(item.item.height)
};
var to = {
x: parseInt(item.element.getAttribute("data-gs-x")),
y: parseInt(item.element.getAttribute("data-gs-y")),
width: parseInt(item.element.getAttribute("data-gs-width")),
height: parseInt(item.element.getAttribute("data-gs-height"))
};
if (from.x != to.x )
{ if(ko.isWritableObservable(item.item.x)) {
item.item.x(to.x);
}else if(!ko.isObservable()){
item.item.x = to.x;
}
}
if (from.y != to.y) {
if (ko.isWritableObservable(item.item.y)) {
item.item.y(to.y);
} else if (!ko.isObservable()) {
item.item.y = to.y;
}
}
if (from.width != to.width) {
if (ko.isWritableObservable(item.item.width)) {
item.item.width(to.width);
} else if (!ko.isObservable()) {
item.item.width = to.width;
}
}
if (from.height != to.height) {
if (ko.isWritableObservable(item.item.height)) {
item.item.height(to.height);
} else if (!ko.isObservable()) {
item.item.height = to.height;
}
}
}
}, 10);
}
});
ko.computed({
read: function () {
fromObs = true;
var widgets = ko.unwrap(valueAccessor().widgets);
var newGridItems = [];
for (var i = 0; i < gridItems.length; i++) {
var item = ko.utils.arrayFirst(widgets, function (w) { return w == gridItems[i].item; });
if (item == null) {
grid.removeWidget(gridItems[i].element);
ko.cleanNode(gridItems[i].element);
} else {
newGridItems.push(gridItems[i]);
}
}
for (var i = 0; i < widgets.length; i++) {
var item = ko.utils.arrayFirst(gridItems, function (w) { return w.item == widgets[i]; });
if (item == null) {
var innerBindingContext = bindingContext['createChildContext'](widgets[i]);
var itemElement = ko.bindingHandlers.gridStack.helpers.cloneNodes(template)[0];
grid.addWidget(itemElement, ko.unwrap(widgets[i].x), ko.unwrap(widgets[i].y), ko.unwrap(widgets[i].width), ko.unwrap(widgets[i].height), true);
ko.applyBindings(innerBindingContext, itemElement)
newGridItems.push({ item: widgets[i], element: itemElement });
} else {
var to = {
x: ko.unwrap(widgets[i].x),
y: ko.unwrap(widgets[i].y),
width: ko.unwrap(widgets[i].width),
height: ko.unwrap(widgets[i].height)
};
var from = {
x: parseInt(item.element.getAttribute("data-gs-x")),
y: parseInt(item.element.getAttribute("data-gs-y")),
width: parseInt(item.element.getAttribute("data-gs-width")),
height: parseInt(item.element.getAttribute("data-gs-height"))
};
if (from.x != to.x || from.y != to.y) {
grid.move(item.element, to.x, to.y);
}
if (from.width != to.width || from.height != to.height) {
grid.resize(item.element, to.width, to.height);
}
}
}
gridItems = newGridItems;
fromObs = false;
},
disposeWhenNodeIsRemoved: element
}).extend({ deferred:true });
ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
gridStack.destroy();
});
return { 'controlsDescendantBindings': true };
}
};
//VM
var vm = {
widgets:ko.observableArray([
{x:ko.observable(0), y: ko.observable(0), width: ko.observable(2), height: ko.observable(2)},
{x: ko.observable(2), y: ko.observable(0), width: ko.observable(4), height: ko.observable(2)},
{x: ko.observable(6), y: ko.observable(0), width: ko.observable(2), height: ko.observable(4)},
{x: ko.observable(1), y: ko.observable(2), width: ko.observable(4), height: ko.observable(2)}
]),
add: function ()
{
vm.widgets.push({ x: 1, y: 2, width: 4, height: 2 });
},
twoWay: function () {
if (vm.widgets()[0].x() == 10) {
vm.widgets()[0].x(0);
} else {
vm.widgets()[0].x(vm.widgets()[0].x() + 1)
}
},
delete: function (item) {
vm.widgets.remove(item);
}
};
ko.applyBindings(vm); HTML ">
<button data-bind="click:add">Add</button> <button data-bind="click:twoWay">twoWay</button> <div class="grid-stack" data-bind="gridStack:{ widgets:widgets }"> <div class="grid-stack-item"> <div class="grid-stack-item-content"> <button data-bind="click: $parent.delete">Delete me</button> <div data-bind="text:'x:'+x()"></div> <div data-bind="text:'y:'+y()"></div> <div data-bind="text:'width:'+width()"></div> <div data-bind="text:'height:'+height()"></div> </div> </div> </div> <script src="test.js"></script> PS .size() jquery was braking on jQuery 3.0 > |
I don't think radiolips nor I are familiar with knockout, so if you can look at latest demo sample and make a PR with the changes for others that would be great, so we could take it. thank you. |
I ran across this in developing a plugin for another framework that uses knockout and this is definitely the better approach. However, when I try to use this example I think it is broken now due to the removal of the jQuery dependencies. When trying to apply the binding I'm getting the error below. I assume this was done in an older jQuery dependant version?
I know your post was real old @cordasfilip, but was wondering if my assumptions are correct in regard to not being a jQuery plugin now? |
It's really old and I haven't used ko.js or grid stack in years, but from what I can see my solution is no longer valid because of JQuery. But I am sure you could make it work since the dependence on jQuery is minimal. |
Thanks, I was looking at the migration guide from 0.6.4 to 1.0.0 ad think the changes will be minimal. |
Might want to take a look at |
Thanks Alain, will definitely take a look. |
* link to issue gridstack#465 so it can be found
closing due to old age and no help updating our demos... added link from doc back here instead. |
The knockout code example is using a knockout component. Knockout components are not used to wrap an external javascript library. Components should only deal with the view model and not anything DOM related to keep a good separation of concerns between the view and the view model (MVVM).
Knockout custom bindings are what is used to wrap external librairies to listen to view model changes and update the state of the external plugin.
I had all kind of problems with the included knockout example from the project page in a real world application. I decided to re-implement it as a custom binding. I am pretty sure this could be useful for other people as well.
I include here a proposed custom binding and how to use it in the view.
And the actual html view:
The "changed" property in the custom binding is called when items changed, this way you can save the widgets to the server etc.
The text was updated successfully, but these errors were encountered: