Inheritance of custom nodes (contd.)

I'm continuing on this topic by @H3llsing:

Other than the .js file, it would be nice to have the possibility to extend the .html sections too.

Is there anything that could be done as of now to reuse, for instance, oneditprepare or oneditsave functions from other nodes?

Thanks!

1 Like

If the functionality is the same then sure. You would need to host the script in a js file and use a standard html script tag to import it.

Hi @Steve-Mcl ,
Thanks for the response. I'm talking about importing from a different node, would that work? And how would you point to js from another node in the .html file?
Thanks!

Your other node would present the js file by way on an admin endpoint. Your other-other nodes would use a standard html script tag to import the JavaScript.

Any nodes' server-side js can generate an admin endpoint for resources to be consumed client side. In fact as of node-red 1.3.x you can just place files in a resources folder for it to be served up to the client. All you need then is the URL and you can consume the client side html script tag.

Note. This is all theory as I haven't tested it (but it works in my head - just haven't had a chance to try it out).

Ps, have a look at the js SRC for the serial node to see an example of an admin endpoint.

Thanks again @Steve-Mcl ,
Adding the .js file in the resources folder seems like a good idea, but it seems to work only if it belongs to an instantiated node. Let me explain:

I'd like to have a library-node that contains the common stuff for all my final nodes. This library-node never gets actually instantiated, as only the final nodes do.
So, it'd be nice if the resource folder with the common .js files would be only in the library-node module, but since this node never gets instantiated it won't serve the .js files.

Is there an elegant way to achieve this?

Thanks!

Well clearly, in order to do something actively, the code has to be run. So if you want to deliver something dynamic, you will need to attach it to Node-RED somehow:

  • In an existing node as an API. That node does not actually need to be added to your flow :slight_smile: Since the act of enabling the node via the palette is enough to load the module which will instanciate the API.
  • Loaded to a global in your settings.js - not tried this but it may be possible to access that from a node's config html file.

There are a few other possibilities that I can think of.

In uibuilder, for example, I now have several modules that are singleton classes. That is where the class is instanciated by the module iteself so that there can only ever be a single instanciation of the class. So that instanciated class (because Node-RED is the host, not your node) is now accessible to ANY node's runtime module. Though you do have to take a little care about order of loading. I instanciate the class at the start of my node's runtime module but I don't actually configure it until a bit later on. This makes use of how node.js loads modules. A module is only actually loaded once no matter how many times you require it.

Take a look at the source code for uibuilder. Especially, have a look at the v4.2.0 branch code because that has been reworked into a structure that is much easier to follow.

So in the future, someone could write a node that made use of one of the classes as long as they made sure to check that the class instance is actually configured (I provide a gettable flag for that). This would allow another node to make use of the socket.io comms that uibuilder uses for example.


In theory, you might even be able to use something like that approach but put the initial require in settings.js which would be called even earlier and wouldn't require a node to be in the palette. Not tried that though but I think it should work. The downside of this would be that you would not have any access to any of the Node-RED features since you would be running the class instance before any of the node-red specific code was active.

In addition to the singleton approach, depending on how your code works, you might be able to get away with static methods and variables in a class instead.

Thanks for your input @TotallyInformation ,
While playing with your suggestions I realised that I was actually wrong: the resource folder of the library-node gets served even if there is no instance of that node.
All I was missing was the "node-red" element in the library-node's package.json file, which I previously omitted since I thought, having no nodes, there was no need for it.

1 Like

Yes, that gives you access to the existing node-red ExpressJS app servers as well as things like access to the node-red settings.

Of course, you could also configure your own separate ExpressJS app as well if you needed to. But I think, in general, if you need to share something into the Editor, it is best to use the existing admin web server as it also aligns the permissions and other security.

It is a fun rabbit hole when you get to look in detail about how node.js modules work, especially in the context of something as complex as Node-RED :grinning:

1 Like

Hi all,
To whoever might be interested, I recently published some nodes on which I tried to push inheritance (if we can call it so) to the max :slight_smile:

Basically, I created a common package that contains all the main logic (server and client side) as well as the HTML templates for the configuration panel and the documentation (help panel):

Then I created the following Node-RED nodes, which all do basically the same with small differences in the paths of the files they use:

If you check their source code on GitHub, you can see how their .js and .html files have very minimal and similar code, that simply load and call functions and templates from the "commons" package, passing few different parameters.

Let me know what you think of this experiment :slight_smile:

Cheers,
Giampiero

1 Like