Dynamic config dashboard

I assume the anwser to this question is going to be uibulder, but still let me ask.

I am building a custom automation process, where define rules, conditions and actions for each rule. This is working so far, now it is time to build a UI to maintain these rules. I need a completely dynamic interface, that generates the UI based on the data that is entered: lists, dropdowns, entry fields.

I would love to see some examples. I have some basic experience with uibuilder, but conceptually I don't yet see how this is going to work.

I assume in this case most of the logic would in on the javascript side and not in Node-Red. Which I guess is not that bad, as it would be easier to debug a complicated logic in Chrome than in a function node.

Also I am very bad at designing a decent UI. Do you know any websites where I can put a basic page together, maybe use some templates and it give me HTML example with CSS? No so much as a complete website, but input forms, etc.

Thanks.

Well, this is probably possible in Dashboard but I don't see that it would give you any benefits and would almost certainly be harder because you will most likely be fighting the underlying AngularJS framework because you need to dynamically create the interface anyway.

This is likely an ideal use-case for UIBUILDER though. :grinning:

The slight downside of using UIBUILDER is that, if I haven't already provided a no-code element that matches one of your needs, you will need to output your own. Thankfully, this isn't too hard but involves a little head-scratching until you've grasped the low-code option JSON format. (Hint: the JSON format for low-code actually mirrors how you would manually create the HTML structure).

Actually, when using either Dashboard or UIBUILDER, you would probably do it in Node-RED. With UIBUILDER though, you can certainly do it either way, whichever you find more comfortable.

There are certainly plenty of online page builders. However, as you are building a data-driven UI, I would recommend starting with the basics. You can make it pretty later. So you might prefer to use something like Figma that will let you draw your layout ideas on-screen as a wireframe without worrying about the underlying HTML. That will let you get a basic layout and help you understand what element types you need. Then you can always come back to the forum for some help on turning them into HTML.

If you do decide to use UIBUILDER, remember that the no-code uib-element outputs the low-code JSON data. So you can get that node to output something similar to what you want and examine the data format to make whatever changes you like. Using the low-code JSON data, you can create litterally ANY HTML UI driven by Node-RED data.

Also remember that you can turn data into HTML in Node-RED itself and save to the appropriate uibuilder folder. This lets you create a basic layout and save it so that it is efficiently loaded. I'm not sure exactly how dynamic your layout will need to be but you will certainly want to separate the fixed and dynamic layout parts. The fixed parts can be written directly as HTML or generated from Node-RED and saved to a fixed file and the dynamic parts either completely re-generated on update OR, amended with new data depending on how much needs to change each time, how big things are, etc.

UIBUILDER provides a cache node as well so that you can have the latest data always available ready for a new or reloaded connection.

Also, the latest version has the front end router available which lets you break down a single-page app style web UI into logical parts should you want that.

Thanks, very useful details. I will check this low-code option out, I am not really sure what that is. But based on the input data I should be able to generate a JSON to represent the screen layout.

Last time I wanted to update uibuilder, I ran into the problem with NodeJS version, and Node-Red version. We said to wait until the next major update. So probably will start with the not-most-up-to-date version.

No problem. Just try out a simple uib-element example and send the output to debug. That will show you what the low-code output looks like. The uibuilder client library or the uib-html node can turn that into full HMTL.

You should be fine with node.js v14.20 or above, if not, I'll issue a fix. For node-red, best to use the latest version. I did have a minor issue not so long back where a dependency ended up with the wrong version but that was fixed. So you should always be safe with the latest version on uibuilder until there is a major version change. These days, I am trying to hold off major version changes until there is a major Node-RED version change. Unfortunately, the next major release of Node-RED is somewhat overdue. I do, however, now keep a list of potential breaking changes in the roadmap documentation so that people get some forewarning.

I am on Node-Red 3.0.2 and uibuilder 5.1.1. This does not have the example yet, but I look it up on Github and download it from there.

Ouch! That is a loooong way behind.

There weren't many breaking changes to the v6 series though so as long as you are on node.js v14.20+ (which is a requirement for node-red v3 anyway), you should be fine to upgrade. And you really should, there are a LOT of bug fixes and improvements along the way.

I just upgraded to 6.8.2 without any issues. Last time this did not work.

I can see the uid-element node, but I am not seeing any example you mentioned. Which example do you recommend looking at?

Phew - glad I wasn't spinning you a yarn there!! :grinning:

Ah, sorry, too close to things, I sometimes forget to explain the details.

image

It is the no-code examples flow that you want.

Jesus! I imported the no-code example. I need a few beers for this, this is not the usual 5 node example...

:rofl: :mage:

It isn't as bad as it looks! Really you only need one of the simple examples and all will come clear. Honestly!

You are right, I am getting through the initial shock, and it is starting to make sense. I think the uib-element is going to be a great generating the dynamic forms to enter the data.

In my layout, I will have a column with a list on the left that show the rules I want to edit, and on the right some complicated forms will be generated based on the rule that got selected on the left.

I got ChatGPT to generate me a skeleton HTML and CSS to get started.
My left column looks like this:

        <!-- Left Column -->
        <div class="left-column">
            <h2>Rules</h2>
            <div class="scrollable-list">
                <!-- Example List Item -->
                <div class="list-item">
                    <div class="icon">📁</div>
                    <div class="content">
                        <h3>Title 1</h3>
                        <p>Some smaller text under it</p>
                    </div>
                </div>
                <!-- Add more list items as needed -->
            </div>
        </div>

I assume I can give an ID to the scrollable-list div and address that in the uib-element to insert the list into the page. In the uib-element I guess in my case, I just pick Type: HTML and use a ui-tempate node to generate the above structure. Right?

What is the use of the Position field in the uib-element? If I inject data, it replaces the entire content, I don't see how either first or last will help me to add more data, like add item to a list? Or it only makes sense for "Add row ..." types?

Let's say I have a piece of text on the screen (like the name of the selected rule) that I want to replace. Simple text inside a div or span. Like .innerHTML; in javascript. Can I also do that with uib-element with Type = HTML and just pass a string in msg.payload?

I think that will be all to begin with.

Certainly.

Yes, you can certainly do that.

I am currently working on some even simpler methods to help insert HTML for the next release. Should make things even easier because you will be able to do

<div class="scrollable-list" uib-topic="some-topic"></div>

Then simply send a msg with the topic and a payload containing the HTML.

Yes, that is what uib-element does. It is a fairly basic thing at present.

Of course, it also has a "list item" element which you can use to add/replace list items. As long as you can choose the appropriate parent element and decide where you put the new/replaced child element, you can do pretty much anything for adding/replacing.

For deleting or updating an existing element, you can use the uib-update node.

So you could use uib-element with the list type, to create the initial list. Use it again to add/replace specific list items. And use uib-update to delete list items or to update the attributes or content of a list item.

The position fields related to the fact that you add things to a page in a hierarchy. So you select the parent. Then when you want to add something, you need to say where in the child nodes. The default is "last" so if you don't specify a position, the new element will be added as the last child of the parent. But you can add it as the first child or at any child number. So you can add a new list item or replace one into the middle of an existing list.

Yes.


Sounds like you are getting there. Just let me know if there is anything you can think of that would help and simplify things for people who follow. :slight_smile:

Thanks for all the feedback.
The uib-topic option sounds simple, especially when the webpage has a more fixed structure and you need to make small change here-and-there.
Maybe what could also be useful is I could update the class of an element. E.g. I need to blink something on the screen, or change the field color if validation fails, hide/show helper text, etc.

Ah, well ... :slight_smile:

Forgot to mention that as well as replacing the innerHTML with the msg.payload, you will be able to specify msg.attributes to make any changes to attributes that you like..

Depending on time, I may also add specific msg.classes and msg.styles as well, we'll see, not strictly necessary but would make life easier.

I'm also considering allowing msg._ui as well so that you can integrate no- and low-code outputs. Slightly more complex because I need ot stop other things from processing it as well. But would be a nice capability I think.

And one more thing: where can I find examples of sending messages to Node-Red which is not buttons or forms? Like in my case when I have a list which contains only div, but I want to make them clickable? How do I format that?

You have 2 options.

You can use uibuilder.eventSend(event) on any event handler either by specifying it in HTML (<div onclick="uibuilder.eventSend(event)">) or in code by adding it as the handler function.

Alternatively, you can create your own custom sends simply by using uibuilder.send(....). Which takes exactly the same form as node-red's send function. Just pass it the msg object to send.

<div id="div01" onclick="myfunc(event)">...</div>
function myfunc(event) {
    uibuilder.send( { topic: "some topic", payload: "from the front-line!", from: event.target.id })
}

Pass the magic event prop if you need to but you can pass anything you like in the function call. event is a "magic" DOM variable and it should be available in the function regardless (though there are moves to kill that feature off in the standards).

The potential advantage of eventSend is that it includes stuff which is potentially hard(ish) to grab from the depths of an HTML Element or the event in code and returns them without effort. The potential downside is that it only gives you whatever I thought might be useful. It includes things like keyboard modifiers though so probably does cover most things.

OK got you. And I can add a special ID to the divs I am adding the onclick="uibuilder.eventSend(event)" event to, so I will know which items in the list if clicked on.

1 Like

I did some coding today, and made pretty good progress with my UIBuilder page. Events uib-element works as I expected.
I am pretty stoked how well it works. And most of the is indeed in Node-Red, did not even touch the javascript so far.

One question thought: I have a function node which generates the initial content for the screen. If I load the screen and trigger that with a function node, all work fine. I also trigger the same function node via a second port of the uibuilder via a switch node which check msg.uibuilderCtrl === "client connect". But it does not seem to do anything. Is there a trick here?

Make sure that you delete the msg.uibuilderCtrl property if going back round into the uibuilder node. To prevent weird control loops, the main node ignores any messages containing it.

Thanks, it is working now :slight_smile:

1 Like