Is there an editor event on wiring up two nodes?

I'd like to create a port-validation mechanism so that some nodes can reject certain nodes upon connection attempts. AFAIK, this would require an editor event that fires up when connecting ports.

Is there an event like that?
I looked at the API reference, but fail to see elaboration on this.

The closest ones look like

links:add

but I'm not sure.

Could someone confirm or point me in the right direction, please?

Thanks!

I think it would be better if you explained a bit more about what you want to achieve as there may be better ways of doing it.

Upstream and downstream nodes are not really meant to know anything about their neighbours but rather just react to incoming messages.

Nodes should not care what is connected before or after in the flow.

It isn't as easy as saying nodes shouldn't care.

That is true in most cases, but there entirely valid cares where nodes are expected to be used in some particular combination. For example, the HTTP In requires there to be an HTTP Response node in the flow to properly end it.

We don't enforce that at all in the nodes themselves but we could have done. Instead we added a rule to nrlint, the flow lint tool, to add a warning for that scenario.

So depending on the specific scenario, there's nothing wrong with a node wanting to help a user wire it up properly if there are some specific constraints that have to be met.

Creating a custom rule for nrlint would be one approach - although we haven't really done enough to publicise nrlint or encourage it's adoption generally.

To do something in the node itself, the links:add rule is the right one to listen for (along with the other links:* events so you know when an invalid link is removed).

2 Likes

Thanks. Linting sounds great during design time. I'll take a look.

@TotallyInformation As for the purpose, our custom node lib includes node clusters that are supposed to work within their own circles, so some kind of barricades are needed. I'd like to improve the affordance with explicit barricades so that people learn the node combination and the correct in/out port mapping with intuition as much as possible.

Although there are the on-hover port tooltips and the help doc pane on the sidebar, those are not straightforward enough for my audience. So I'm looking for more obvious options.

1 Like

OK, I think I can understand that.

Not sure if this helps but for uibuilder, I eventually needed an event mechanism that could be reused across nodes. As the recommendation has always been not to use Node-RED's event handlers for custom nodes, I created a reusable library that I use now in a few nodes. That lets me define my own events and I think that would meet your needs too. It wasn't hard to set up and it isn't much code. I used the Events2 library to enable wildcard handling of event "topics".

TotallyInformation/ti-common-event-handler: A common, shared event handler used across multiple TotallyInformation packages (github.com)

1 Like

I've been trying to make the link:add event handler work. But this is the first time I work with Editor events and the doc didn't say exactly where I should register the event handler from. So I tried to place it in one of my custom nodes like this:

module.exports = function(RED) {

    RED.events.on("link:add", function(link) {
        console.log("A link has been added to the workspace!");
    })

    function MyNode(config) {
        ....
    }
}

Then when I added a node into my flow, wired it up to another node, and deployed the flow, nothing happened about that event handler.

I then moved it inside the node function and it was still the same.

So my question is: Where should I put this handler?

I've also seen warning elsewhere on the forum that

You need to be careful making use of RED.events since this is used internally by Node-RED. It would be easy to end up with a clashing event name and that could be super hard to diagnose.

This looks pretty scary. So I'd love to have advice on how to go about this.
Is nrlint still the preferred way than listening to these events?

If you want to listen for events in the editor, then you need to place the code in your .html of a node (or a plugin..).

It appears you have added it to the .js file - which is the runtime side of things and which doesn't have any access to the editor events.

Interesting!
I added the code in mynode.html

<script type="text/javascript">
    RED.events.on("link:add", function(link) {
        console.log("A link has been added to the workspace!");
    });

and still don't see the output in the browser JS console when I change the wire of that node.

Below is the screenshot of the console log after the attempt.

Screen Shot 2022-09-02 at 16.50.40

Ok, we found it.

onpaletteadd and onpaletteremove are where we should insert those event handlers.

If this is correct, I really wish details like this could be in the API doc.

Not so. If you correct your event to links:add instead of link:add, you will see that as the Editor loads, you get a ton of messages. No need to do that in onpaletteadd.

Here is the start of the Editor script code for one of my test nodes:

// Isolate this code
(function () {
    'use strict'

    /** Module name must match this nodes html file @constant {string} moduleName */
    const moduleName  = 'feedme'
    /** Node's label @constant {string} paletteCategory */
    const nodeLabel  = moduleName
    /** Node's palette category @constant {string} paletteCategory */
    const paletteCategory  = 'Totally Information' // 'advanced-input'
    /** Node's background color @constant {string} paletteColor */
    const paletteColor  = '#C0DEED' // '#F6E0F8' //'#E6E0F8'

    function runtimeCommsHandler(event, dataAsString) {
        console.log('>>> MSG FROM RUNTIME', dataAsString, event)
    }

    // Can only push from the runtime to the editor, not the other way around
    RED.comms.subscribe('jk-test-runtime', runtimeCommsHandler)

    console.log('hi')
    RED.events.on('links:add', function(link) {
        console.log("A link has been added to the workspace!", link)
    })
    RED.events.on('links:remove', function(link) {
        console.log("A link has been removed from the workspace!", link)
    })

The events also fire immediately, no deploy is needed.


UPDATE: OK, so this is actually HARD! Ideally, you just want to know if a link is added AFTER the Editor is loaded. Node-RED will signal links:add when the Editor is loaded for all existing wired nodes.

So somehow you need to exclude all those events.

Then the links:add event is triggered when ANY link is added to ANY node. You can limit processing to the TYPE of node you are interested in but you cannot tie the processing to the "current" node because the Editor does not give you access to that information. Only the runtime has that & it only has it for the node instance code so you can't even pass it back in the registerType settings option. Nor could you create an API in the runtime.

Then you have the issue that you need to be able to handle nodes that haven't yet been deployed.

So at the moment, my conclusion is that there appears to be no way to achieve what you are asking for.

Hopefully someone with a bigger brain can work something out.

Possibly you could create a runtime API that is called from the links:add event handler in the Editor only if the source type is the correct type. On the runtime side, the API would need to retrieve the correct node(s) via the RED API and process the logic. The return would be a 200 code for success or some other code along with possibly some reason data if not a success and the event handler would then display an appropriate user message.

Complex.

1 Like

Wow. Thanks so much for digging into this @TotallyInformation !
I got the similar feeling as yours: Since links:add is designed as watching over all links, it's hard to implement callback for individual nodes without more elaborate custom filtering and state management.

Tough.

Why?

If the requirement here is to add the concept of a 'bad' link because of what it is connecting, then you'd want the editor to be able to flag up any link considered bad. You wouldn't want to assume that every deployed link is 'good' - so you'd want to be able to revalidate all links when the editor is opened.

I'm not sure I follow this @TotallyInformation. What is the 'current' node in this context? Why are you talking about the runtime when we're talking purely about events in the editor?

If you're requirement is to flag up 'bad' links that break your rules, then why do you want callbacks for individual nodes?

RED.events.on("link:add", function(link) {
   if (link.source.type === 'my-node-type' || link.target.type === 'my-node-type') {
      // this is a link that connects to a node type I care about... do whatever
      // additional checks you need about whether this is a 'good' link or not.   
   }
})

When I said "all", I meant all irrelevant

After re-thinking, you are probably correct that all that is needed is for ALL of the particular source node types to be checked. That certainly simplifies things in that you only need to check each source node of type x for links that connect to target types y, z, etc. Throwing up a notification for all links in error.

I was more thinking about the user interactions and highlighting which node(s) are in error. But in fact, this might not be needed.

Depending on how many different checks you need to do and how many source/target combinations, you probably want to keep the data in an object or even an API lookup if you anticipate having a lot and/or needing them to change regularly.

"links:add"