Alternative of current ui nodes in the FlexDash dashboard

If you use uibuilder, you can use it along-side Dashboard. Though you can only have a single Dashboard per Node-RED instance, you can have as many uibuilder nodes as you like. The only restriction is that they (and the Dashboard) all have to be on unique URL's.

If you want, you can keep the Dashboard on the normal Node-RED port but put all of the uibuilder nodes on a different port (only 1 though, you can't use multiple ports for uibuilder).

So you are saying, the user has to figure out all that, because both old and new dashboards are using the SAME port.

So WHY on earth wouldn't be a simple solution to change the default port of the new FlexDash to

localhost:1882

to make it "beginner friendly" ?

I've just looked at Dashboard setting.
There are 3 TABs: Layout / Site / Theme.
Checked all of them, but do not find ANY sign of port config

I think something is lost in translation here. Just to be clear "Dashboard" (capital D, node-red-dashboard) is the set of dashboard nodes mostly maintained by Dave. uibuilder (node-red-contrib-uibuilder) lets you build data-driven web UI's of any sort. Including dashboards (small d).

Every web server can offer pages on a URL path. The Dashboard nodes happen to only allow a single instance on a single path. uibuilder can offer multiple paths either via the Node-RED port (same as used by Dashboard) or via its own ExpressJS instance (using a different port).

If you want more complex or less complex scenarios, you can use a reverse proxy to hide/change the ports and the paths.

I didn't mention FlexDash, that is something different. It isn't developed by me so I can't comment on it.

That is correct, Dashboard (capital D) uses the same port as Node-RED - always. You can change the path but not the port.

Why does it matter what port the new dashboard is on? What matters is the path. The default for the current dashboard is /ui. Flex Dash might be /dash. It will no doubt be configurable as the current one is.

Why would anything have to be obsoleted because of the new dashboard?

3 Likes

Hi @PizzaProgram,
Not sure if you really need to second port number?

@tve has made his Flexdash very customizable. For example I can create multiple config nodes, one for each dashboard I would like to have:

image

So I get this:

image

And this:

image

So very easy to have multiple dashboards running in parallel, each with their own UI nodes...

2 Likes

And indeed like @Colin says, you can just run your old dashboard in parallel on the "/ui" path on the same Node-RED system:

image

2 Likes

And uibuilder :grinning:

Just make sure that all of your paths are unique.

1 Like

Because there was a discussion about maybe implementing a different Socket.io or something that is more responsive, etc.
That would have had caused trouble with the original dashboard.

But this is the past, now I see, @BartButenaers did an EXCELLENT job :+1: as he has showed on the prev. picture about :1990/... port.

I can not say how glad I am :slight_smile:
Thank you very much!

1 Like

It's perfectly possible to run multiple versions of socket.io concurrently on the same port. I do that every day with FlexDash... The only reason I'm aware of to use a port other than the standard one for the dashboard is security or other reverse-proxying type of configurations.

1 Like

It is to have different ExpressJS settings and middleware mostly.

Well, it took several restarts but finally I do have a Vite dev server integrated into Node-RED :tada: It all still feels a little wonky but I'll be using it in the coming days and will hopefully shake out some issues. Also, I'm pretty sure it doesn't yet work under Windows (unless you use docker) due to the \ path delimiter: I tried to use path.join everywhere but I know I missed a couple of spots (most notably glob always uses / delimiters), so some fixing will be needed.

If anyone want to give it a spin to report usability issues and errors, the installation is pretty simple:

  1. Download the repo of the nodes your will be developing. For the sake of this demo, do a git clone https://github.com/flexdash/node-red-fd-testnodes.git (you can also download just the sources)
  2. Install the node-red-fd-testnodes repo in a throw-away node-red install, e.g., do the npm install /home/me/src/node-red-fd-testnodes linking as you would with any other node repo you want to dev. Then start Node-RED.
    Alternatively, if you have docker launch a Node-RED container using the following fun command line (you have to adapt the "/home/me/src" part of the -v option, and you can change the port if you have another Node-RED running, e.g. -p 1990:1880):
    docker run --rm -ti -p 1880:1880 \
      -v /home/me/src/node-red-fd-testnodes:/usr/src/node-red/node-red-fd-testnodes --entrypoint \
      bash nodered/node-red:2.2.2 -c \
        "npm install ./node-red-fd-testnodes; npm start --cache /data/.npm -- -userDir /data"
  1. Open the Node-RED editor and in the palette grab a FD dev server node, drag it into a flow, and configure it:

    • create a FlexDash config, the defaults work fine for a throw-away test
    • enable the dev server in the FD Dev Server config (the checkbox is unchecked by default)
    • (I know that the "point your browser at" help text doesn't work yet)
  2. Deploy and watch the status under the dev server node, it'll show "npm install" for perhaps a minute while it runs npm install in the FlexDash sources (this downloads some 250MB). You can also watch progress in the node-red log

  3. Once the dev server shows a green running status, point your web browser at http://localhost:1880/flexdash-src (adjust the port as needed).

  4. You should see a "FlexDash loading..." page which will sit there for about a minute(!) while Vite compiles all of FlexDash, Vue, and Vuetify. This happens whenever the dev server is started.

  5. Eventually you will see FlexDash loading widgets and a "Welcome to FlexDash -- This is an empty dashboard..." widget. This is your FlexDash test instance!

Now let's do some development work to see hot-module-reload in action:

  1. Let's work on the TestButton widget. First drag a FD testbutton node into your flow and deploy just the changed nodes or changed flow. (If you deploy everything then Vite restarts and you'll have to wait a minute again...)
  2. You will immediately see the button appear in the dashboard. Its color will be dark red, unless you changed it when you configured the node.
  3. Now open widgets/test-button.vue in the node-red-fd-testnodes directory that you downloaded in step 1. This is the source code for the button widget. An easy change is to replace color: this.color, on line 34 by color: '#00ff00',, this causes the color setting to be ignored and always render the button in green.
  4. Save the test-button.vue file in your editor, you should instantaneously see the color of the button in the dashboard change to green. That's hot-module-reload for you...

For extra bonus, you can try out the debugging/inspection using the Vue browser plug-in:

  1. You will need to install the Vue dev plugin (chrome version, firefox version) and probably restart the browser(?).

  2. If you again navigate to the dashboard, you can open the dev tools (ctrl-shift-I) and should have a "Vue" devtools tab. Activate that and click on the "target" icon in the upper-right-hand corner of the devtools, then select your button widget in the dashboard.

  3. You will see the hierarchy of components on the page and if you click on the <TestButton> instance one up from the highlighted <VBtn> you will see something like:

  4. Notice how the bindings color show the #00ff00. This Vue inspector is the most useful feature of the dev plugin because it allows you to see how values propagate and what your widget does with them.

My plan is to write a widget -> node generator that produces a (functional) scaffold for a node based on a widget. This should make it easier for me to produce nodes for all the existing widgets... The main feature will be to dynamically generate a config tab for the widget's properties that can be included in the node's HTML. That'll keep me busy for a bit...

Step one of auto-generating the NR editor edit panel is to parse the widget source code. Thanks to some horrible RegExps and hacky use of new Function() I have a proof of concept! :nerd_face:

Step two is to prototype a tabbed editor layout so FD node authors can separate configuration of the NR node and configuration of the widget. That's looking pretty good so far.
Here's the node config tab:

And here's the widget props tab:

Next step will be to auto-generate the widget tab content. I'm thinking that the FD config node can do the following:

  • set-up filesystem watches on vue files in FD node modules
  • parse changed vue files
  • produce NR editor html in the module's resources subdir
  • load the HTML dynamically from the resources inside the widget tab

This leaves it pretty much to the user to decide whether to include the auto-generated tab or not. So there's full flexibility and convenience. :boom:

Hi @tve,
I did not have the chance to test anything, but I have some questions.

Could you explain in short what this Vite server does? I am not familiar with this kind of technologies...
And above in this discussion your wrote "I see two options to address this...". Which of both solutions did you choose? I assume the first one...
And am I correct that only developers need to have the Vite dev server node in their flow?

Personally I am not a big fan of those tabsheets. Will try to explain why:

  1. I truly can understand that you - as Vue frontend developer - see a clear separation of Node-RED config and the Vue widget config. However when I saw this morning your screenshot, I thought immediately: what are widget properties? When I see a UI node in my flow, then that node represents my widget. So all the properties in the config screen are there to configure that widget. So for me personally I liked your config screen without tabsheets more. But others may think different about that!!

  2. Since the config screens of all UI nodes should have a similar look and feel, I would have to add those tabsheets also on the config screen of my own UI nodes. But when I look e.g. at the SVG-UI node, it currently already has a lot of tabsheets:

    image

    Personally I would find it very confusing when I have to start adding all those tabsheets below the "Widget properties" tabsheet. Moreover how do I determine which config is Node-RED config or Widget config. Because some properties in my config screen will have impact on the server-side of my UI node. Is that Node-RED config then, or should I position it all under the "Widget" tabsheet?

Would be nice if some other folks could share their opinion about the tabsheets...

That is a clever idea, so you will be able to offer a series of widgets out of the box.
Since you setup file-system watches, I assume that the parsing happens at run time?
And that it does not work like this: there is a new Vue version supported by Flexdash, so you run a generator once to recreate your ui nodes...

My zero-Vue knowledge makes it hard to understand this. In the original discussion there was an idea from @dceejay that in a new dashboard every ui node would be a config node, but that a basic set of ui nodes (button, gauge, ...) would be installed out of the box. Do I understand correctly that this won't be the case now? That you still have two kind of nodes:

  • A basic set of standard ui nodes whose config screen will be generated automatically (at runtime)?
  • The contrib ui nodes that we can develop.

The issue that I see here is that ui node developers won't be able to look at the code standard nodes, to see how it is implemented there. Because it works entirely different. I mean: I cannot use the auto generation in my ui nodes, because I have lot of other stuff on my config screen...

That is not clear to me what you mean by that. Could you explain it a bit more please?

Hopefully you don't my feedback as negative, I just need to digest all the web development stuff a bit on my very-early-sunday-morning :wink:

Sure! When you write code for the browser the standard process is that the code you write isn't what is actually sent to the browser, rather, it gets transformed first. Kind'a like a program in a compiled language gets compiled and linked before you get to run it. For browsers the process generally includes:

  • convert typescript (or other languages and syntactic sugar, such as Vue's single-file-components) to pure javascript/html/css
  • add polyfills (compatibility shims) for older browsers
  • figure out which code is actually referenced and needs to be shipped to the browser
  • group small files into larger ones to optimize load time
  • minify & compress

There are several "bundlers" that perform the above steps, webpack and rollup are two of the more popular ones. But this bundling is a problem when you're developing because it can easily take several minutes to complete and you don't want to wait that long for each edit-test cycle.

So what Vite does is act as a web server that does the minimum necessary transformations on-the-fly as the browser is requesting stuff. So if the browser requests a .js file vite may serve it up as-is, but if the browser requests a .vue file then vite quickly splits the html, javascript and css portions and serves up something that pulls all three pieces in an appropriate way. You could think of this as being similar to just-in-time compilation vs. up-front compilation.

In addition to the transformations vite also watches your source files so if you change a source file it automatically retransforms it and sends the updated version to the browser. There a small piece of code swaps out the old code for the new code as seamlessly as possible. This is hot-module-reload: your modified code is running immediately without reloading the page. Of course this has limitations 'cause the new code may not work with the old data structures, but it's awesome for many small changes, in particular when you make small CSS and layout tweaks.

Yes and yes.

Yup, these are all valid points. I don't know either, so I'm leaving all options open. :sunglasses: An argument in favor of having the 'widget props' tab is that it makes clear which settings can be overridden by a message's props field. But for the auto-generated widgets having a node config tab with just name and FD isn't very nice either.

Sure! I haven't pushed the updated code to github because too much is still in flux, so you can't see it yet. I also don't really know what the workflow should look like :thinking:, I only know what some of the pieces in it probably should do...

Abstractly speaking, the automatic code generation produces two classes of artifacts: the content and the boilerplate glue. The content ones contain the information gathered from the widget: param names, types, defaults, help text. This is the stuff you don't want to manually keep in sync and want re-generated automagically. Then there's the boilerplate glue you may want to change so you can put everything together to your liking and you don't want those changes to be clobbered by auto re-generation.

Let me take a specific example: the gauge widget. The code generator produces two files:

  • resources/gauge-props.html with the the content of the widget props fields
  • gauge.html with the std Node-RED editor boilerplate

You want the first file to regenerate each time you change the props definitions in the .vue file. But while you may want to use the second file as a starting point, you want to be able to customize it to your liking.

So, starting the example at the beginning... the gauge widget .vue file is the source of everything and contains:

props: {
    max: { type: Number, default: 100, tip: "maximum value" },
    color: { type: String, default: 'green', tip: "color of filled segment" },
    ...

The generated resource file (see Loading extra resources in the editor) resources/gauge-props.html contains:

<div class="form-row">
    <label for="node-input-max">Max</label>
    <input type="text" id="node-input-max" class="fd-typed-input" input-type="num"/>
    <input type="hidden" id="node-input-max-type" />
    <small class="fd-indent">Maximum value. Default: 100</small>
</div>
<div class="form-row">
    <label for="node-input-color">Color</label>
    <input type="text" id="node-input-color" class="fd-typed-input" input-type="str"/>
    <input type="hidden" id="node-input-color-type" />
    <small class="fd-indent">Color of filled segment. Default: "green"</small>
</div>

You may recognize that this is what you normally type manually for each node configuration parameter. Now comes the boilerplate, which is in gauge.html. First we need an html element into which we insert the above form-row stuff:

    <!-- tab for the widget params -->
    <div id="fd-tab-props" style="display:none"></div>

and then we need to insert it dynamically, which we do in the oneditprepare function. The insertion is a one-liner using the JQuery .load() method, although the generated html expects the use of typedInput fields so there's a magic fdInitTypedInput callback function to do that:

    oneditprepare() {
      const url = 'resources/@flexdash/node-red-fd-testnodes/gauge-props.html'
      $('#fd-tab-props').load(url, fdInitTypedInput)

So where does that leave you? The generated gauge.html file only contains boilerplate, you can take it as-is, or you can modify it, or you can completely replace it. In all cases, you decide whether and how you pull in the auto-generated gauge-props.html with the individual form-row divs. For example, you can:

  • disregard gauge-props.html and write your own form-row html
  • modify gauge.html to remove the tabs yet still include the gauge-props.html
  • pull in gauge-props.html and then hide some of the form-rows because you don't want the user to set those because your code in the node computes them
  • pull in gauge-props.html and then tweak the types allowed by some of the typedInput fields

I didn't address it specifically, but my plan is for the node-red-fd-core module with the "standard" widgets & nodes to be no different from any "contributed" module. There should be a separate node-red-fd-samples repo with sample widgets that are suited to hack up into your own.

Something I don't understand yet is how exactly Node-RED loads modules and thus what my options are to hook into that process and generate stuff on-the-fly. As a result I don't know yet when what should get generated. The thing I do know is that I'll get it wrong and we'll have to figure out how best to do it. :wink:

Phew, I hope this long post was helpful...

The palette is getting crowded:

One minor problem I'm having is that all these nodes are in node-red-fd-corewidgets and even though that npm package depends on node-red-flexdash and the latter indeed gets installed by npm, its nodes are not added to the palette. The result is that the core widgets are unusable because they need the config node in node-red-flexdash. Is there a way to tell the NR package manager to load the nodes defined in a dependent package?

1 Like

@tve,
Hopefully I have interpreted your problem correctly. My node-red-contrib-ui-web-push node depends on the node-red-web-push node from another author. Since my ui node requires the config node from that other node, I have added that node to my package.json file:

    "dependencies": {
        "node-red-contrib-web-push": "^0.0.3"
    },

And that works fine, as long as you don't uninstall that other node manually of course...

1 Like

Your node doesn't seem to be in the NR flows library, so you can't really tell what happens if you install it using the node-red package manager :thinking: I'm gonna pull it in and then test... Hmmm, can you provide a link to the npm module?

Due to circumstances I have never published it on npm ...
And now it is a bit too late for that, as a result of the huge progress you made with FlexDash.
Because when I release it now, people will start asking support. And I have no time for that at the moment...

Perhaps I can create a local package for it tomorrow evening. See here. Not sure if that is 100% waterproof for your test...

When the time comes, you may read first this article again, because it just changed in the last 6 weeks:
https://nodered.org/docs/creating-nodes/packaging

And here you can Add a node to the palette:
https://flows.nodered.org/add/node