{"action":"created","issue":{"url":"https://api.github.com/repos/troolee/gridstack.js/issues/62","labels_url":"https://api.github.com/repos/troolee/gridstack.js/issues/62/labels{/name}","comments_url":"https://api.github.com/repos/troolee/gridstack.js/issues/62/comments","events_url":"https://api.github.com/repos/troolee/gridstack.js/issues/62/events","html_url":"https://github.com/troolee/gridstack.js/issues/62","id":56834882,"number":62,"title":"Knockout Component Two Way Binding Update Help","user":{"login":"ascendantofrain","id":781671,"avatar_url":"https://avatars.githubusercontent.com/u/781671?v=3","gravatar_id":"","url":"https://api.github.com/users/ascendantofrain","html_url":"https://github.com/ascendantofrain","followers_url":"https://api.github.com/users/ascendantofrain/followers","following_url":"https://api.github.com/users/ascendantofrain/following{/other_user}","gists_url":"https://api.github.com/users/ascendantofrain/gists{/gist_id}","starred_url":"https://api.github.com/users/ascendantofrain/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ascendantofrain/subscriptions","organizations_url":"https://api.github.com/users/ascendantofrain/orgs","repos_url":"https://api.github.com/users/ascendantofrain/repos","events_url":"https://api.github.com/users/ascendantofrain/events{/privacy}","received_events_url":"https://api.github.com/users/ascendantofrain/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-02-06T16:39:03Z","updated_at":"2015-02-06T21:58:49Z","closed_at":null,"body":"Ive got my dashboard application wired up using the knockout component code snippet; however, when I drag or resize a widget in the grid, the viewmodel is not being notified of the updated values for each tile.  Any thoughts and help would be greatly appreciate.\r\n\r\n**Component**\r\n\r\n    (function() {\r\n    'use strict';\r\n\r\n    ko.components.register('dashboards-tiles', {\r\n        viewModel: {\r\n            createViewModel: function(controller, componentInfo) {\r\n                var ViewModel = function(controller, componentInfo) {\r\n                    var grid = null,\r\n                        gridstack = $('.grid-stack'),\r\n                        gridstackWidth = gridstack.width(),\r\n                        widgetHeight = (gridstackWidth / 12) - 5;\r\n\r\n                    this.widgets = controller.widgets;\r\n\r\n                    this.afterAddWidget = function(items) {\r\n                        _.each(items, function(item) {\r\n                            item = $(item);\r\n\r\n                            if (item.data('_gridstack_node') || item[0].nodeType !== 1) {\r\n                                return;\r\n                            }\r\n\r\n                            if (grid == null) {\r\n                                grid = $(componentInfo.element).find('.grid-stack').gridstack({\r\n                                    auto: false,\r\n                                    animate: true,\r\n                                    always_show_resize_hande: true,\r\n                                    cell_height: widgetHeight,\r\n                                    handle: '.editing-options .handle',\r\n                                    vertical_margin: 10\r\n                                }).data('gridstack');\r\n                            }\r\n\r\n                            grid.add_widget(item);\r\n                            ko.utils.domNodeDisposal.addDisposeCallback(item[0], function() {\r\n                                grid.remove_widget(item);\r\n                            });\r\n                        }, this);\r\n                    };\r\n\r\n                    $(componentInfo.element).find('.grid-stack').on('resizestop', function(event, ui) {\r\n                        setTimeout(function() { // necessary as resizestop fires slightly too early\r\n                            _.each(Highcharts.charts, function(chart) {\r\n                                if (chart) {\r\n                                    chart.reflow();\r\n                                }\r\n                            });\r\n                        }, 10);\r\n\r\n                        // TODO: update viewmodel rect values after resizestop\r\n                    });\r\n\r\n                    $(componentInfo.element).find('.grid-stack').on('dragstop', function(event, ui) {\r\n                        // TODO: update viewmodel rect values after dragstop\r\n                    });\r\n                };\r\n\r\n                return new ViewModel(controller, componentInfo);\r\n            }\r\n        },\r\n        template:\r\n        [\r\n            '<div class=\"grid-stack\" data-bind=\"foreach: {data: widgets, afterRender: afterAddWidget}\">' +\r\n                '<div class=\"grid-stack-item\" data-bind=\"attr: { \\'data-gs-x\\': x, \\'data-gs-y\\': y, \\'data-gs-width\\': width, \\'data-gs-height\\': height, \\'data-gs-min-height\\': rect.minHeight, \\'data-gs-max-height\\': rect.maxHeight, \\'data-gs-min-width\\': rect.minWidth, \\'data-gs-max-width\\': rect.maxWidth }\">' +\r\n                    '<div class=\"grid-stack-item-content\">' +\r\n                        '<h3>' +\r\n                            '<span data-bind=\"text: title\"></span>' +\r\n                            '<span class=\"link float--right\" data-bind=\"click: $root.removeTile\">x</span>' +\r\n                        '</h3>' +\r\n                        '<img src=\"Content/images/loading.gif\" data-bind=\"visible: isLoading()\" class=\"loading\" />' +\r\n                        '<!-- ko if: editMode -->' +\r\n                        '<div class=\"tile-options\">' +\r\n                            '<div class=\"row\">' +\r\n                                '<label>Title <input type=\"text\" class=\"text-input\" data-bind=\"value: title\" /></label>' +\r\n                            '</div>' +\r\n                            '<div class=\"row\">' +\r\n                                '<label>View <select data-bind=\"options: $root.views, optionsText: \\'ViewName\\', optionsCaption: \\'Choose a view\\', value: selectedView\" class=\"text-input\"></select>' +\r\n                                '</label>' +\r\n                            '</div>' +\r\n                            '<div class=\"row\">' +\r\n                                '<label>Table/Chart <select class=\"text-input\" data-bind=\"options: (selectedView() || {}).Charts, optionsText: \\'ChartName\\', value: selectedChart\"></select>' +\r\n                                '</label>' +\r\n                            '</div>' +\r\n                            '<div class=\"row\">' +\r\n                                '<button type=\"button\" class=\"btn btn--small\" data-bind=\"click: $root.saveTile\">Save Tile</button> or <span class=\"link\" data-bind=\"click: $root.cancelTile\">Cancel</span>' +\r\n                            '</div>' +\r\n                        '</div>' +\r\n                        '<!-- /ko -->' +\r\n                        '<div data-bind=\"chart: chart\" class=\"fill\"></div>' +\r\n                        '<div class=\"editing-options\">' +\r\n                            '<span class=\"handle\"><i class=\"icon icon-drag\"></i> Drag to Move </span>' +\r\n                            '<span data-bind=\"click: $root.editTile, visible: !editMode()\" class=\"link\">Edit </span>' +\r\n                        '</div>' +\r\n                    '</div>' +\r\n                '</div>' +\r\n            '</div>'\r\n        ].join('\\n')\r\n    });\r\n    })();\r\n\r\n**View Model**\r\n\r\n    var pfp = pfp || {};\r\n    pfp.models = pfp.models || {};\r\n    pfp.models.dashboards = pfp.models.dashboards || {};\r\n\r\n      (function() {\r\n    'use strict';\r\n\r\n    this.sizes = {\r\n        small: { width: 1, height: 1 },\r\n        medium: { width: 2, height: 2 },\r\n        large: { width: 3, height: 3 }\r\n    };\r\n\r\n    this.Dashboard = function(d) {\r\n        var self = this,\r\n            defaults = {id: 0, name: '', tiles: []},\r\n            config = _.extend(defaults, d);\r\n\r\n    this.Tile = function(t) {\r\n        var self = this,\r\n            sz = pfp.models.dashboards.sizes;\r\n\r\n        t = t || {};\r\n\r\n        self.title = ko.observable(t.title);\r\n        self.size = ko.observable(t.size);\r\n        self.viewId = ko.observable(t.viewId);\r\n        self.chartId = ko.observable(t.chartId);\r\n        self.isWalk = t.isWalk;\r\n        self.showChart = ko.observable(true); //for custom binding\r\n        self.chart = ko.observable();\r\n        self.selectedView = ko.observable();\r\n        self.selectedChart = ko.observable();\r\n        self.editMode = ko.observable(false);\r\n        self.isLoading = ko.observable(false);\r\n\r\n        self.x = ko.observable(t.column);\r\n        self.y = ko.observable(t.row);\r\n        self.width = ko.observable(t.width === 0 ? 2 : t.width);\r\n        self.height = ko.observable(t.height === 0 ? 3 : t.height);\r\n\r\n        self.rect = {\r\n            minWidth: 4,\r\n            minHeight: 3,\r\n            maxWidth: 12,\r\n            maxHeight: 6\r\n        };\r\n\r\n        self.clearChart = function() {\r\n            pfp.core.charts.clearChart(self);\r\n        };\r\n    };\r\n    }).call(pfp.models.dashboards);\r\n\r\n\r\n\r\n**Markup**\r\n\r\n    <dashboards-tiles params=\"widgets: tiles, nextRow: nextAvailableRor\"></dashboards-tiles>"},"comment":{"url":"https://api.github.com/repos/troolee/gridstack.js/issues/comments/73320367","html_url":"https://github.com/troolee/gridstack.js/issues/62#issuecomment-73320367","issue_url":"https://api.github.com/repos/troolee/gridstack.js/issues/62","id":73320367,"user":{"login":"crashnick16","id":10521649,"avatar_url":"https://avatars.githubusercontent.com/u/10521649?v=3","gravatar_id":"","url":"https://api.github.com/users/crashnick16","html_url":"https://github.com/crashnick16","followers_url":"https://api.github.com/users/crashnick16/followers","following_url":"https://api.github.com/users/crashnick16/following{/other_user}","gists_url":"https://api.github.com/users/crashnick16/gists{/gist_id}","starred_url":"https://api.github.com/users/crashnick16/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/crashnick16/subscriptions","organizations_url":"https://api.github.com/users/crashnick16/orgs","repos_url":"https://api.github.com/users/crashnick16/repos","events_url":"https://api.github.com/users/crashnick16/events{/privacy}","received_events_url":"https://api.github.com/users/crashnick16/received_events","type":"User","site_admin":false},"created_at":"2015-02-06T21:58:49Z","updated_at":"2015-02-06T21:58:49Z","body":"I noticed this issue too.\n\nSince the widgets are bonded in a for each with the attribute binding knockout will not know about the move events as these are only triggering a jquery change event. \n\n\n\nSent from my iPhone\n\n> On 6 Feb 2015, at 16:39, Adam Verheyen <notifications@github.com> wrote:\n> \n> Ive got my dashboard application wired up using the knockout component code snippet; however, when I drag or resize a widget in the grid, the viewmodel is not being notified of the updated values for each tile. Any thoughts and help would be greatly appreciate.\n> \n> Component\n> \n> (function() {\n> 'use strict';\n> \n> ko.components.register('dashboards-tiles', {\n>     viewModel: {\n>         createViewModel: function(controller, componentInfo) {\n>             var ViewModel = function(controller, componentInfo) {\n>                 var grid = null,\n>                     gridstack = $('.grid-stack'),\n>                     gridstackWidth = gridstack.width(),\n>                     widgetHeight = (gridstackWidth / 12) - 5;\n> \n>                 this.widgets = controller.widgets;\n> \n>                 this.afterAddWidget = function(items) {\n>                     _.each(items, function(item) {\n>                         item = $(item);\n> \n>                         if (item.data('_gridstack_node') || item[0].nodeType !== 1) {\n>                             return;\n>                         }\n> \n>                         if (grid == null) {\n>                             grid = $(componentInfo.element).find('.grid-stack').gridstack({\n>                                 auto: false,\n>                                 animate: true,\n>                                 always_show_resize_hande: true,\n>                                 cell_height: widgetHeight,\n>                                 handle: '.editing-options .handle',\n>                                 vertical_margin: 10\n>                             }).data('gridstack');\n>                         }\n> \n>                         grid.add_widget(item);\n>                         ko.utils.domNodeDisposal.addDisposeCallback(item[0], function() {\n>                             grid.remove_widget(item);\n>                         });\n>                     }, this);\n>                 };\n> \n>                 $(componentInfo.element).find('.grid-stack').on('resizestop', function(event, ui) {\n>                     setTimeout(function() { // necessary as resizestop fires slightly too early\n>                         _.each(Highcharts.charts, function(chart) {\n>                             if (chart) {\n>                                 chart.reflow();\n>                             }\n>                         });\n>                     }, 10);\n> \n>                     // TODO: update viewmodel rect values after resizestop\n>                 });\n> \n>                 $(componentInfo.element).find('.grid-stack').on('dragstop', function(event, ui) {\n>                     // TODO: update viewmodel rect values after dragstop\n>                 });\n>             };\n> \n>             return new ViewModel(controller, componentInfo);\n>         }\n>     },\n>     template:\n>     [\n>         '<div class=\"grid-stack\" data-bind=\"foreach: {data: widgets, afterRender: afterAddWidget}\">' +\n>             '<div class=\"grid-stack-item\" data-bind=\"attr: { \\'data-gs-x\\': x, \\'data-gs-y\\': y, \\'data-gs-width\\': width, \\'data-gs-height\\': height, \\'data-gs-min-height\\': rect.minHeight, \\'data-gs-max-height\\': rect.maxHeight, \\'data-gs-min-width\\': rect.minWidth, \\'data-gs-max-width\\': rect.maxWidth }\">' +\n>                 '<div class=\"grid-stack-item-content\">' +\n>                     '<h3>' +\n>                         '<span data-bind=\"text: title\"></span>' +\n>                         '<span class=\"link float--right\" data-bind=\"click: $root.removeTile\">x</span>' +\n>                     '</h3>' +\n>                     '<img src=\"Content/images/loading.gif\" data-bind=\"visible: isLoading()\" class=\"loading\" />' +\n>                     '<!-- ko if: editMode -->' +\n>                     '<div class=\"tile-options\">' +\n>                         '<div class=\"row\">' +\n>                             '<label>Title <input type=\"text\" class=\"text-input\" data-bind=\"value: title\" /></label>' +\n>                         '</div>' +\n>                         '<div class=\"row\">' +\n>                             '<label>View <select data-bind=\"options: $root.views, optionsText: \\'ViewName\\', optionsCaption: \\'Choose a view\\', value: selectedView\" class=\"text-input\"></select>' +\n>                             '</label>' +\n>                         '</div>' +\n>                         '<div class=\"row\">' +\n>                             '<label>Table/Chart <select class=\"text-input\" data-bind=\"options: (selectedView() || {}).Charts, optionsText: \\'ChartName\\', value: selectedChart\"></select>' +\n>                             '</label>' +\n>                         '</div>' +\n>                         '<div class=\"row\">' +\n>                             '<button type=\"button\" class=\"btn btn--small\" data-bind=\"click: $root.saveTile\">Save Tile</button> or <span class=\"link\" data-bind=\"click: $root.cancelTile\">Cancel</span>' +\n>                         '</div>' +\n>                     '</div>' +\n>                     '<!-- /ko -->' +\n>                     '<div data-bind=\"chart: chart\" class=\"fill\"></div>' +\n>                     '<div class=\"editing-options\">' +\n>                         '<span class=\"handle\"><i class=\"icon icon-drag\"></i> Drag to Move </span>' +\n>                         '<span data-bind=\"click: $root.editTile, visible: !editMode()\" class=\"link\">Edit </span>' +\n>                     '</div>' +\n>                 '</div>' +\n>             '</div>' +\n>         '</div>'\n>     ].join('\\n')\n> });\n> })();\n> View Model\n> \n> var pfp = pfp || {};\n> pfp.models = pfp.models || {};\n> pfp.models.dashboards = pfp.models.dashboards || {};\n> \n>   (function() {\n> 'use strict';\n> \n> this.sizes = {\n>     small: { width: 1, height: 1 },\n>     medium: { width: 2, height: 2 },\n>     large: { width: 3, height: 3 }\n> };\n> \n> this.Dashboard = function(d) {\n>     var self = this,\n>         defaults = {id: 0, name: '', tiles: []},\n>         config = _.extend(defaults, d);\n> \n> this.Tile = function(t) {\n>     var self = this,\n>         sz = pfp.models.dashboards.sizes;\n> \n>     t = t || {};\n> \n>     self.title = ko.observable(t.title);\n>     self.size = ko.observable(t.size);\n>     self.viewId = ko.observable(t.viewId);\n>     self.chartId = ko.observable(t.chartId);\n>     self.isWalk = t.isWalk;\n>     self.showChart = ko.observable(true); //for custom binding\n>     self.chart = ko.observable();\n>     self.selectedView = ko.observable();\n>     self.selectedChart = ko.observable();\n>     self.editMode = ko.observable(false);\n>     self.isLoading = ko.observable(false);\n> \n>     self.x = ko.observable(t.column);\n>     self.y = ko.observable(t.row);\n>     self.width = ko.observable(t.width === 0 ? 2 : t.width);\n>     self.height = ko.observable(t.height === 0 ? 3 : t.height);\n> \n>     self.rect = {\n>         minWidth: 4,\n>         minHeight: 3,\n>         maxWidth: 12,\n>         maxHeight: 6\n>     };\n> \n>     self.clearChart = function() {\n>         pfp.core.charts.clearChart(self);\n>     };\n> };\n> }).call(pfp.models.dashboards);\n> Markup\n> \n> <dashboards-tiles params=\"widgets: tiles, nextRow: nextAvailableRor\"></dashboards-tiles>\n> —\n> Reply to this email directly or view it on GitHub.\n> \n"}}