I made some progress with the settings tonight:
I've made some more progress now but I have encountered a slight problem.
The jinja template creates a dasboard by reading widget settings like this:
<div class="grid" data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
<div class="grid-sizer"></div>
<!-- ko foreach: widgets -->
<div class="widget w1 h1" data-bind="appendWidget: $data, attr: {'widget-id': id}, text: name"></div>
<!-- /ko -->
</div>
I have created a custom knockout binding handler "appendWidget" like this:
ko.bindingHandlers.appendWidget = {
init: function(element, valueAccessor) {
$grid.append( element ).packery( 'appended', element );
//Make it draggable
var draggie = new Draggabilly( element );
$grid.packery( 'bindDraggabillyEvents', draggie );
}
};
I think this is the right approach when injecting DOM elements and it almost works. The widgets are injected to the DOM and bound to the grid as expected. The problem is that i get a knockout error right after that:
packed_core.js?b00f909b:16147 Could not bind view model DashViewModel to target #tab_plugin_dash : Error: Unable to process binding "foreach: function(){return settingsViewModel.settings.plugins.dash.dashboards }"
Message: You cannot apply bindings multiple times to the same element.
I have no clue why this happens. Any ideas?
The source is available here: https://github.com/StefanCohen/Dash
For such cases I disabled the minified and bundled feature in my development-settings.
config.yaml
devel:
virtualPrinter:
enabled: true
forceChecksum: false
okAfterResend: false
okWitchLinenumber: false
webassets:
bundle: false
minify: false
So that I can debug the 3rd party sourcecode or I just add some console.info lines the the 3rd party sourcecode like knockout.js.
Hi @Stefan_Cohen,
I checked out your sourcecode and received a different message:
Could not bind view model DashViewModel to target #tab_plugin_dash : ReferenceError: Unable to process binding "foreach: function(){return settingsViewModel.settings.plugins.dash.dashboards }"
Message: Unable to process binding "foreach: function(){return widgets }"
Message: Unable to process binding "attr: function(){return {'widget-id':id} }"
Message: id is not defined
Ah. I have missed to update the the default config with a bunch of settings (including the ID).
it should look like this in config.yaml:
dash:
dashboards:
- name: main
widgets:
- animate: ''
enabled: true
id: '1'
link: somelink
name: first
src: https://s3-us-west-2.amazonaws.com/s.cdpn.io/82/one-world-trade.jpg
target: _blank
type: image
units: ''
value: somevalue
widgetSize: w1 h1
- animate: ''
enabled: false
id: '2'
link: ''
name: second
src: https://s3-us-west-2.amazonaws.com/s.cdpn.io/82/cat-nose.jpg
target: _blank
type: image
units: ''
value: ''
widgetSize: w2 h2
- animate: ''
enabled: false
id: '3'
link: ''
name: third
src: https://s3-us-west-2.amazonaws.com/s.cdpn.io/82/flight-formation.jpg
target: _blank
type: image
units: ''
value: ''
widgetSize: w1 h1
dataSources:
- enabled: true
headers:
- name: X-Api-Key
value: My API Key
- name: Test
value: Some value
method: get
name: OctoPrint
pollingInterval: 1
type: json
url: http://localhost/api/job
- enabled: true
name: System Clock
pollingInterval: 1
type: clock
hmm..I think the problem is the $grid-element append
, because you append into the foreach-section new elements by your own and this is what KO doesn't like.
If I put the grid identifier above the foreach-loops it works...kind of...in my case it adds three widgets and then I receive again an error-message.
<div class="grid">
<div data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
...
Maybe thats the right direction...maybe
Thanks. That is the same kind of error I get. I have a feeling that this has something to do with my binding handler.
I originally tried by just letting the template produce the widgets but I couldn't find a way to add them to the packery grid.
Then I looked at the afterRender method but I couldn't get it to work either. That's when I found this in the Docs: https://knockoutjs.com/documentation/template-binding.html#note-4-using-afterrender-afteradd-and-beforeremove
Sometimes you might want to run custom post-processing logic on the DOM elements generated by your templates. For example, if you’re using a JavaScript widgets library such as jQuery UI, you might want to intercept your templates’ output so that you can run jQuery UI commands on it to transform some of the rendered elements into date pickers, sliders, or anything else.
Generally, the best way to perform such post-processing on DOM elements is to write a custom binding,
It is working now...I have no idea if it should looks/act like this: three black boxes, draging around. But everything works without an error
I just moved the $grid outside the loop.
<div class="grid">
<div class="grid-sizer"></div>
</div>
<div data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
<div class="grid-sizer"></div>
<!-- ko foreach: widgets -->
<div class="widget w1 h1" data-bind="attr: {'widget-id': id}, appendWidget: $data, text: name"></div>
<!-- /ko -->
</div>
What kind of problem do you have with the "afterrender" approach? Because this sound like the right solution. You modify needed attributes via template that jquery needs (widget-id).
I have an other idea: build your on "afterRenderWidget"-function like this:
- separate creating the layout
- then assign jquery-functionality
<!-- do layouting -->
<div data-bind="foreach: settingsViewModel.settings.plugins.dash.dashboards">
<div class="widget w1 h1" data-bind="attr: {'widget-id': id} />
</div>
<!-- do assign jquery stuff -->
<div data-bind="assignWidgets: settingsViewModel.settings.plugins.dash.dashboards" style="display:none">
ko.bindingHandlers.assignWidgets = {
...
now you can iterate over the given array of all dashboards, grab the widgets via $ selector and add the
packery and Draggabilly (without append, because already there)
What do you think?
Thanks! I will try that right away.
Update: That first method worked but I think I will explore your second idea since I think it will work much better with multiple dashboards.
Update 2: Oh. I think I just realised what I did wrong. I'm calling .append from the binding handler but the widget is already there. I should just be able to call the packery "appended" method and then use your second idea with the hidden div to init the layout once all dashboards & widgets have been inserted on the page.
Thats what I try to say
Good luck!