Dashboard v2 ideas

I made some progress with the settings tonight:

2 Likes

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 :slight_smile:

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 :slight_smile:

Good luck!

1 Like