Alternative of current ui nodes in the FlexDash dashboard

Just to complete my observation:

After a restart of nodered the gauge is indeed showing -100 to 100.

1 Like

Morning (here in Belgium) @tve,
Personally I am not a fan of that. Because it would disrupt the uniform user experience. And because developing a ui node would become more different from developing a non-ui node. And there are already not much ui node developers...

Do I undestand correctly that the only problem is, that the Node-RED config is not typed (i.e. all values are strings)? In that case I see a few alternatives:

  1. Start a new discussion to ask why it is not typed? I assume it cannot be changed in the core, but then at least we know it. This has also caused a lot of headache for me in the past...
  2. We just make an agreement in the documentation that the ui node developer needs to cast all numbers/booleans/...
  3. You change your function input, so we need to pass a config like this:
    var params = {
       {
          "min": someValue,
          "type": "Number"
       },
       ...
    }
    
    fd.updateWidget(this, params);
    

I don't think that is correct to be honest. I've never had issues with config panel entries when passing between the front-end and the runtime node. So I'd say that the method of transfer is converting things to a string, not Node-RED itself?

Are you sure about that? When you have on your config screen an html input element with type number, then it will arrive on the server (via the config parameter) as string. And then you need to cast it explicit to a number with parseInt or parseFloat? Same for true/false. Or perhaps I am just getting nuts now...

Is this not an html5 input "feature" ? (IE part of the http spec ?) I thought they had to return strings.

Yup, but it's the node red editor that grabs the values and puts them into this, so I expected it to do the conversion as opposed to letting the implementation of each and every node battle this...

If they are set as (node-red) typed inputs then yes - but if standard html input boxes then no.

1 Like

Hmmm, that is not my experience... This is my gauge edit panel:

image

I initialize the typed inputs with:

    oneditprepare() {
      for (const k of ['value', 'arc', 'min', 'max']) {
        $("#node-input-"+k).typedInput({type:"num", types:["num"]})
      }
    },

and in the node constructor I have:

  function flexdashTestGauge(config) {
    const fd = RED.nodes.getNode(config.fd) // get a handle onto FlexDash
    RED.nodes.createNode(this, config)
    console.log('flexdashTestGauge:', JSON.stringify(config))

and the output I get is:

flexdashTestGauge: {"id":"1cf748462f03af58","type":"flexdash testgauge","z":"56494f06ecf796f0","fd":"ad4328.f698dcd8","name":"counter gauge","value":null,"unit":"","title":"count","color":"#00ee00","arc":80,"min":"-20","max":100,"x":620,"y":140,"wires":[]}

That looks like strings to me, no numbers in sight.... What am I doing wrong?

UPDATE: mystery solved, the type input field is required and not optional (I thought I didn't need it since I know the type).

2 Likes

Hey Dave,
That is good to know. Thanks! Weird that I have never noticed that...
So yet another good reason to use TypeInputs.
It is a pity that there is no TypedInput for colours, because UI nodes might use that a lot.
Although perhaps a standard html input of type color might be enough, since it has to return a string anyway...

1 Like

OK, so I looked again at my own code. I did a console.log of the incoming config and this was the result for uibuilder:

0|Node-RED  | {
0|Node-RED  |   id: '06370e67a8450029',
0|Node-RED  |   type: 'uibuilder',
0|Node-RED  |   z: '8b105c093f631770',
0|Node-RED  |   name: '',
0|Node-RED  |   topic: '',
0|Node-RED  |   url: 'http-vue-loader',
0|Node-RED  |   fwdInMessages: false,
0|Node-RED  |   allowScripts: false,
0|Node-RED  |   allowStyles: false,
0|Node-RED  |   copyIndex: true,
0|Node-RED  |   templateFolder: 'vue-simple',
0|Node-RED  |   extTemplate: '',
0|Node-RED  |   showfolder: false,
0|Node-RED  |   useSecurity: false,
0|Node-RED  |   allowUnauth: false,
0|Node-RED  |   allowAuthAnon: false,
0|Node-RED  |   sessionLength: 432000,
0|Node-RED  |   tokenAutoExtend: false,
0|Node-RED  |   reload: false,
0|Node-RED  |   sourceFolder: 'src',
0|Node-RED  |   deployedVersion: '5.0.0-dev.2',
0|Node-RED  |   x: 570,
0|Node-RED  |   y: 140,
0|Node-RED  |   wires: [ [ '2010a24a50d2386c' ], [ '94c9fd20a1439f2d' ] ]
0|Node-RED  | }

Note that booleans and numbers come through exactly as those types. However, I also note that I wrap the only number input as this.sessionLength = Number(config.sessionLength) || 120 // in seconds so maybe I have had an issue with it at some point.

1 Like

Well, I ran aground with my hot-module-reload... :exploding_head: So I figured I might as well tackle a seemingly more difficult problem :laughing: : custom nodes, i.e., the equivalent of ui-template :sunglasses:

I can report that it works! Here's a screen shot of my demo flow in the background with a FD "reset" node added (bottom-left). The rocket icon indicates this is a custom node. The editor pane shows the beginning of the Vue component source code. It's a slightly simplified version of the button widget. So now I have three buttons (up, down, and reset) where the first two are standard flexdash buttons and the last one is a flexdash custom used to implement a button :tada:

Now I need to package everything back up and publish...

NB: this little detour also told me how to fix the issues with the hot module reload and how to link in "contrib" fd nodes, so I'm not dead yet...

NNB: One thing I'm wondering is whether this is the right model. This custom node creates a widget component and then instantiates it once. I wonder whether there's a better model that allows a custom widget to be defined and then instantiated multiple times. Maybe one node representing the widget and then one node per instance? Or is that already too complicated for NR users to grok?

3 Likes

Good question. Some other approaches:

  1. You move the code editor input field to a config node. Then multiple nodes can use the same config node, i.e. the code is developed once and reused in multiple nodes. But perhaps that is not a good approach, because it has some disadvantages also:

    • The list of config nodes will become very long for users that have lots of ui-template nodes (i.e. difficult to search).
    • You need to navigate to your config node, to be able to see your code.
  2. I think that lots of users would put your ui-template node into a subflow, in order to be able to use it multiple times. Don't know how your node will behave in those cases? I won't use it that way, but just wanted to mention that here (in case it might be relevant for your design decisions...).

EDIT: you have already a config node, and I don't think you can use multiple config nodes currently.

That's a really good question! If each instance of a node in a subflow gets a unique ID, which I assume it does, then everything should be peachy and you should end up with one widget per subflow instance.

New demo

I packaged up the custom node stuff and published. I also simplified one step in trying out the demo! So to try it out:

  1. You must have Node-RED >= 2.0.6 with Node.js >= v14 (v12 chokes on require('./foo.js') ), see below for docker instructions if that's of interest
  2. In Node-RED admin go to 'manage palette' and flip to the 'install' tab
  3. Search for flexdash and install node-red-flexdash and node-red-fd-testnodes, you need versions >= 0.2.7 and >= 0.0.5, respectively
  4. Open the "Import" panel, select the examples tab, and import the flexdash-fd-testnodes up-down-counter example
  5. Open the dashboard: http://localhost:1880/flexdash if you have a vanilla install, and if not, open the flexdash config node (config nodes side-panel) and use the link below the 'path' input field.
  6. There's now a "reset" button, looks like the other ones...
  7. If you go back to the flow-editor and edit the "reset button" node you can see the source code
  8. In the source code, change the default color of the button on line 28 from 'primary' to 'blue', deploy, and refresh the dashboard (it doesn't hot-reload yet), the button should now be blue showing that the code got recompiled&loaded

I did incorporate most of the feedback from @BartButenaers and @jodelkoenig , thank you! (I may have missed something, need to do another polishing pass...)

(Next I'll be asked "so, how do I write a Vue component?"... That's a good question!)

Hi @tve,

I have tested it and it looks nice again!!.

Only a few remarks:

  1. The counter still does start counting automatically after a browser refresh, like in the previous version. I assume you did not have time yet to fix this...

  2. I don't use template nodes myself, but I assume people will ask for syntax highlighting. Not sure if that is possible for this kind of coding. Of course not life-threatening, but just mentioning it.

  3. To be honest, it is not clear to me yet how the payload param, props, methods, bindings, ... work. But I assume it will become more clear after reading some examples in the future. I am just wondering if it would be possible/useful to put e.g. the params on an extra tabsheet in the config screen of the node. Not sure...

People that use the current AngularJs UI-Template node frequently, will be able to give you more useful feedback. Hopefully some of them join to test...

BTW a bit off-topic, but perhaps you might have ideas about this: for the current AngularJs dashboard, I use the node-red-contrib-plugin-header all the time. It is a plugin that displays a button in the title bar of the flow editor, which opens the dashboard (running at .../ui) in a new browser tab. I first thought we could create a new plugin by simply replacing "/ui" by "/flexdash":

image

However your setup allows multiple dashboards (at separate url's), so a simple button won't work. So I assume it should be a dropdown (one button for each config node, i.e. for each flexdash dashboard url), but not sure. I need to think about this... Again not urgent ...

1 Like

'been thinking about next steps and reading code... :exploding_head: I believe I should work on making it possible to develop npm modules that contain node-red nodes plus associated widgets. I.e., something like my node-red-fd-testnodes but including the flexdash widget .vue files as opposed to using widgets built into FD.

The fundamental difficulty of the entire enterprise is that it's a dynamic linking problem. The core of FlexDash contains tons of components (all of vuetify, uPlot, and FD components) and the dynamically loaded components need to be able to reference all that.

I see two options to address this. The first side-steps the problem and treats FlexDash and all imported node-red-fd-* modules as one monolithic application. The way it would work is as follows:

  • For development node-red runs the vite dev server, which would serve FD and the external modules up from source, providing debugging and hot-module reload. From starting vite it takes just under a minute to load up FD on my medium-fast box, reloading the browser is fast but not instantaneous.
  • For production, whenever a node-red-fd-* module is installed, node-red runs the vite bundler to produce an optimized distribution bundle of FD + all installed modules and then serves that as static files. The bundling operation takes 1-2 minutes depending on the box it runs on and would have to execute whenever a new NPM module with widgets is installed/upgraded.

The second option is to compile and bundle each node-red-fd-* NPM module separately and dynamically load it into FD. For this purpose there's a Vue sfc-compiler which turns a .vue file into JS/HTML/CSS that can then be loaded into the browser. I'd have to integrate the sfc-compiler into node-red (or run it as external process), produce js/html/css files that can be stored in the node-red-fd-* modules, then be able to dynamically load this stuff into FlexDash, including hot-reload for development.

The nice thing about the first option is that I basically have it working :sunglasses: and I believe it's fairly straight-forward overall. The downside is that every instance of node-red rebuilds FlexDash plus whatever modules are installed from source. The nice thing about the second option is that the end result is clean binary NPM modules that can be published and whomever imports them just ends up getting some static files that node-red serves up. The downside is that it's a pile of work, including some pretty obscure stuff.

Hmm, after writing all this and rethinking it in the process, I think I'm going to go with option #1. Someone can always build #2 later when we get tired of the rebuild step. The node-red-fd-* NPM modules look the same either way (unless they reach into FD internals that option #2 doesn't expose).

If anyone has experience with this stuff and can chime in, I'm all ears!!!

NB: I also would appreciate any input on how to persist the FlexDash configuration (a pile of JSON). Right now it uses a context store, which is not very user friendly. Alternatively I could write it to a file in the data directory or I could figure out how to throw it into the flow.json. I don't have a good grasp of pros and cons.

The Node-RED survey learned us that about 68% of the users install Node-RED on Raspberry Pi 2 & 3. I assume those are slower than your box?

Sorry cannot help you from this point on... Perhaps you should create a separate discussion, containing "Vue bundling" in the subject. Because some experienced users use Vue in uibuilder, and they probably don't read this long FlexDash discussion.

I will have a look tonight (late). Perhaps also create a separate discussion, where you explain what you want to achieve and what your problem is.

KISS - i think - its not a showstopper to start with a JSON file in the NR directory and if needed later migrate to something like a SQLLite DB etc if it becomes onerous.

Craig

2 Likes

I'd like to ask the developers:
If you are publishing a new dashboard,

Please make default PORT a different one, than current dashboard's !

So make them BOTH work, without excluding one the other.

I think it's not much to ask to think about for a second:

  • Staying compatible with OLD devices (dropped phones, old tablets, etc) will reduce waste + pollution on earth.

uibuilder has the ability to use either the same web engine as Dashboard or a different one. Using the same one has less overhead, using a different gives you full control.

Sorry, I don't understand what you mean.

  • Can I have 2 dashboards,
  • working at the same time
  • one (with the new engine) on latest phones
  • an other (old one) on a 5 year old tablet

?