Using a client side JS file

When building some custom nodes, I want to keep them separate in multiple files:
custom1.js, custom1.html, custom2.js, custom2.html

However they share a lot of common functions in the editor (show/hide elements etc.)

How can I make use of my own JS file to make this available in the UI?

If you have a look at the source code for node-red-contrib-uibuilder you will see some examples.

  • nodes/tilib.js is a generic helper module that can be required.
  • nodes/uiblib.js is more specific to uibuilder but is used in the same way.
  • nodes/socket.js and nodes/web.js are both singleton class modules (only a single instance of the class can ever be created). This provides a great way to isolate code & data but ensure that multiple instances of a node always shared the same instance of the class. In this case, processing for Socket.IO and ExpressJS respectively are encompassed by the modules & no matter how many uibuilder nodes you add to your flow, you will always share the same data.

Thank you for chipping in.

Sorry for not being clear:

I do know how to share JS files on server side implementation (had some fun commonJS vs. ES6 modules, which I would prefer).

What I was wondering:

How to share client side JavaScript functions. There's no obvious way how to do a <script src=...></script>

There are a few ways to achieve this but ultimately you have to serve the code via an endpoint.

Here is an example from one of my nodes - I like this pattern as I can share the js with both client and server side.

  1. In your project, add folders /static/, /static/js/ & /static/css/

  2. Add a "dumb" node-red node called (for example) app.js - this is the end point that serves files from /static/ to http://xxxxx/image_tools/static/***

    module.exports = function (RED) {
     /**
      * Enable http route to static files
      */
      RED.httpAdmin.get('/image_tools/static/*', function (req, res) {
         var options = {
             root: __dirname + '/static/',
             dotfiles: 'deny'
         };
         res.sendFile(req.params[0], options);
      });
    }
    
  3. Add the associated html file app.html - this dynamically loads your files at client side (via the end point created in app.js)

    <script type='text/javascript'>
       $.getScript('image-tools/static/js/image_tools.js');
    </script>
    
  4. In package.json, add a node-red node pointing to app.js

    "node-red": {
        "nodes": {
            "app": "app.js",
            "xxx": "xxx.js"
        }
    },
    
  5. create your shared js file. NOTE: in order to be able to import it both server and client side, you need to be a bit clever. This is the basics...

    (function(exports){
    
      exports.getOptionsList = function(){
          return [];  //snipped for brevity
      };
      exports.getOptionsMap = function(){
          return {}; //snipped for brevity
      };
    
    }(typeof exports === 'undefined' ? this.image_tools = {} : exports));
    
  6. To use the js client side, you simple access image_tools.getOptionsMap()

  7. To use the js server side, you require the file const image_tools = require("./static/js/image_tools");

I dont think i missed anything but if I did, have a look through the source of node-red-contrib-image-tools where I employ this solution.

2 Likes

There is a much easier way of doing it since Node-RED 1.3.

Create a directory called resources at the top level of your node's module.

Any file under that directory will then be served up to the editor from the path /resources/<module-name>/<filename>

In your node html, add a script tag to load the file:

<script type="text/javascript" src="resources/your-module/your-file.js"></script>

Note there is no / at the start of the src path.

4 Likes

Ahh, nice. I was thinking something like automatic serving of resources would be a great feature - and it already exists :flushed:. Glad I didnt create a feature request now :rofl:

Cheers Nick.

2 Likes

Hi @Stwissel - so you should now have 2 solutions.

What I would like to say is if you intend on using this node only on new node-red installations (V1.3.x and above) then Nicks answer is the way to go. I would just like to add, if you do, it might be wise to specify the minimum node-red version in your package.json to avoid unnecessary bad feedback

    ...
    "node-red": {
        "version": ">=1.3.0",
        "nodes": {
            "mynode": "mynode.js"
        }
    },
    ...

However if your node is to be used by others who may be on earlier versions, you will have to DIY it with your own endpoint

@knolleary - please correct me if I am wrong on this point.

2 Likes

Awesome. My weekend project: fixing the Cloudant nodes

@Stwissel fixing them how?

Are you aware of the plan we had in place to update the cloudant nodes with Adam Hammond's cloudantplus set of nodes? The goal being to reduce how many forks we're out there?

It has stalled somewhat once I left IBM, but no reason for it not to get revived and the planned work completed.

Adam gave me the green light, signalling he would accept my pull request.
Reworked it to use the newish IBM SDK. Also split the nodes into individual files
for easier update. Made it work with IBM IAM....
and put structure in place to addd mocha test (in GitHub Actions they run nicely since I can run CouchDB side container automatically). Still some work to go....

Sneak preview here:

1 Like

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.