uibuilder.onChange in index.html

I am working with tabulator tables and uibulder and all is working fine, however I am experimenting with inserting javascript from node-red to the front-end using uibuilder. Since uibuilder inserts into index.html I wonder whether this is possible. While I was able to successfully insert table data for tabulator, I noted the data was a json object.

Now I would like to insert javascript code into index.html.

First I attempted putting the code itself into index.html as a script - however when inserting code that works in index.js, it does not run in index.html;

uibuilder.onChange('msg', (msg) => {
   var tabledata = msg.payload;
    other code...
    });

works in index.js, but in index.html gives me a 'uibuilder not defined error' - can this be remedied?

Hi, thanks for using UIBUILDER.

You should note that allowing "random" code to be sent from the server to the client is, of course, something of a security minefield. For this reason, browsers intentionally make this hard to accomplish. And rightly so.

However, it IS possible to dynamically send code to the browser via UIBUILDER. The method is outlined here:

With more detail here:

You basically need to use UIBUILDER's low-code capabilities.

If you are ever getting that error, it will be for 1 of 2 reasons:

  1. The code you've added is executing BEFORE the uibuilder client library has executed.
  2. You are using modern JavaScript MODULES which self-isolate themselves.

If you are using Modules (AKA "ESM"), let me know and I can walk through how to use them correctly - this is a JavaScript thing by the way, not a uibuilder issue.

Assuming you are using the currently default IIFE version of the uibuilder client library and not ESM, you most probably haven't understood the impact of the defer attribute in the default html code.

Any way, if you use uibuilder's built-in dynamic code loading capability, you should avoid the issues anyway. That's because, the client library will attach and execute the new code for you using standard browser API's.

To use that kind of approach - which I don't recommend - you would need that code to run after the uibuilder library has loaded. The easiest approach is to simply use the provided index.js file for your code as that is already in the correct place in the index.html file and just needs uncommenting.

Then you would need to PARSE the transferred code. You cannot transfer certain things verbatim using http, websockets, or (as uibuilder uses) Socket.IO. Things like code and JavaScript Date objects can't be directly transferred. What happens is that those things are translated to strings (or simply removed). So to get code in the way you've started, you would need it to be a string on the Node-RED side and passed to an eval function. You can read up on that function online and see why it is very much disliked by all.

I don't use eval in the client library, a more robust HTML API is used. Even so, you should be very cautious about passing code over and make certain that it cannot do harm to the client. You could, of course, do the same yourself - but it is complex and why bother when I've already done the hard work for you. :smile:

[TotallyInformation] TotallyInformation
https://discourse.nodered.org/u/totallyinformation Regular
2 January

thank you for that informative response. I will stay aware of
minefields. I have reviewed the documentation and noted especially
the area with respect to "load".

As a test, I have started by injecting the following code, using a
subset as noted in the docs;

In an injection node, included as js format, I have the following;


{
"_ui": {
"method": "load",
"txtScripts": [
"function fred() { console.log('HEY! This script loaded dynamically.') }",
"fred()"
         ]
     }
}

inserted as msg._ui directly into the uibuilder node.
The uibuilder node works when I send HTML code to the front-end, adding
or changing HTML either using mustache template with uib-element node,
or directly while using the inject node.
However using the example above, there appears to be no response, no
error, and no output on the console.
There was some mention by the docs and by you of socket.io, so I
installed that. Then I got the following response in the console;

For the time being my goal is simply to send a simple javascript to
the front-end and have it execute.

No, please check the documentation - the socket.io client is already included in the uibuilder client library - that is the only additional JavaScript library you need to include.


I just tested your example code and it worked perfectly, I got the expected output in the browser console when the message was sent.

So a couple more questions:

  • What versions of uibuilder, Node-RED and NodeJS are you using?
  • Can you please share your HTML and JS files.

[TotallyInformation] TotallyInformation
https://discourse.nodered.org/u/totallyinformation Regular
3 January

skytrackerdave:

There was some mention by the docs and by you of socket.io
<http://socket.io>, so I
installed that.

No, please check the documentation - the socket.io http://socket.io
client is already included in the uibuilder client library - that is
the only additional JavaScript library you need to include.


I just tested your example code and it worked perfectly, I got the
expected output in the browser console when the message was sent.

So a couple more questions:

  • Can you please share your HTML and JS files.

index.html;


<!doctype html>
<html lang="en"><head>

     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <link rel="icon" href="../uibuilder/images/node-blue.ico">

     <title>Blank template - Node-RED uibuilder</title>
     <meta name="description" content="Node-RED uibuilder - Blank template">

     <!-- Your own CSS (defaults to loading uibuilders css)-->
     <link type="text/css" rel="stylesheet" href="./index.css" media="all">

     <!-- #region Supporting Scripts. These MUST be in the right order. 
Note no leading / -->
     <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
     <script defer src="./index.js">/* OPTIONAL: Put your custom code in 
that */</script>
     <!-- #endregion -->

</head><body class="uib">
     <h1 class="with-subtitle">uibuilder Blank Template</h1>
     <div role="doc-subtitle">Using the IIFE library.</div>

     <div id="more"><!-- '#more' is used as a parent for dynamic HTML 
content in examples --></div>


</body></html>

index.js;


/** The simplest use of uibuilder client library
  * See the docs if the client doesn't start on its own.
  */

// Listen for incoming messages from Node-RED and action
// uibuilder.onChange('msg', (msg) => {
//     // do stuff with the incoming msg
// })

FWIW I removed socket.io and jquery from uibuilder

I just realized my problem - I had double tags in the inject - I inserted using msg._ui and then my code also started with "_ui" - once I updated by code to

{
"method": "load",
"txtScripts": [
"function fred() { console.log('HEY! This script loaded dynamically.') }",
"fred()"
]
}

all was well. Thank you for your attention to my confusion

1 Like

Great, glad you worked it out. :smile:

If it is of any comfort, I do similar things myself from time-to-time!

Just a quick heads-up - I'm just finalising v7.1 of uibuilder now, should be published soon. A couple of nice new features and the usual bevy of bug fixes and code/documentation improvements.