Limiting config node to one instance

is there a method to ensure that the editor is limited to only one config node of a type? The specific use case sets up a listener on a specific port so creating more than one instance of the config node makes little sense.

It is not

the user can create any number of instances of your node as they wish.
you will need to manage this your self, and design the config node to handle such a situation.

thank you Marcus.

Do you or others have advice on how to manage that process within the custom code? It would seem that somehow, to protect the user from herself/himself, I need to find a way of getting all the nodes of a certain type and ensuring they return the equivalent of a singleton. is there an API call to return those nodes?

Hi @jpadie

In your config Node UI logic.
You can fetch already deployed “config” nodes.

And if a config node exists with the same type…. Scream at the user to not deploy the one that is moments away of being deployed

RED.nodes.eachConfig((c) => {})

I am using my phone currently, but each config returned will have its type (and everything else)

So if your config type is in the loop, they have already deployed a config of your type

EDIT
No longer using my phone.

/* oneditprepare maybe? */
RED.nodes.eachConfig((c) => {
    if (c.type === 'your-config-type') {
        alert(`Ehhh! you have one already (${c.name}), you sure you know what you're doing`)
    }
});

Yep, it's possible with:

onadd: function() {
  let hasConfig = false
  RED.nodes.eachConfig(function (n) {
    if (n.id === 'some-fixed-id')
      hasConfig = true
    });

  if ( !hasConfig ) {
    var configNode = {
      id: "some-fixed-id",
      _def: RED.nodes.getType("your-config-node-type"),
      type: "your-config-node-type",
      users: []
    };
    RED.nodes.add(configNode);
    RED.nodes.dirty(true);
  }
}

Basically when the user adds the node, this function will check if the node exists and if not create it. You don't have to create a selection input.

If you check out some of the code in the uibuilder node, you will see that it has checks to ensure that the URL setting is unique across all instances. This may give you some clues as to how to approach it. The relevant code lives in a common library:

That at least lets you disable the "Done" button and lets you display a warning to users.

Thank you all who responded.

I like the look of the silently added config but (@GogoVega ) I am unfamiliar with the onadd method and where its hook is. Is there any documentation to describe its use?

thank you
Justin

Woops sorry it's a editor property like oneditprepare.

It's not documented but is part of:
https://nodered.org/docs/creating-nodes/node-html

thank you @GogoVega

is it a synonym for onpaletteadd?

testing at this end onadd does not appear to be called when adding a node from the palette to the editor. nor on deploy.

oneditprepare
Executed before a node (in the flow) is opened up for editing.

onpaletteadd
Executed when your node is indexed, and available to be used from the palette.

So you want to attach to onpaletteadd to create a config, which is triggered AFTER your node is installed.

I think that is approach one should take.
I don't use onpaletteadd so could be wrong

  • category: (string) the palette category the node appears in
  • defaults: (object) the editable properties for the node.
  • credentials: (object) the credential properties for the node.
  • inputs: (number) how many inputs the node has, either 0 or 1.
  • outputs: (number) how many outputs the node has. Can be 0 or more.
  • color: (string) the background colour to use.
  • paletteLabel: (string|function) the label to use in the palette.
  • label: (string|function) the label to use in the workspace.
  • labelStyle: (string|function) the style to apply to the label.
  • inputLabels: (string|function) optional label to add on hover to the input port of a node.
  • outputLabels: (string|function) optional labels to add on hover to the output ports of a node.
  • icon: (string) the icon to use.
  • align: (string) the alignment of the icon and label.
  • button: (object) adds a button to the edge of the node.
  • oneditprepare: (function) called when the edit dialog is being built. See custom edit behaviour.
  • oneditsave: (function) called when the edit dialog is okayed. See custom edit behaviour.
  • oneditcancel: (function) called when the edit dialog is canceled. See custom edit behaviour.
  • oneditdelete: (function) called when the delete button in a configuration node’s edit dialog is pressed. See custom edit behaviour.
  • oneditresize: (function) called when the edit dialog is resized. See custom edit behaviour.
    :point_down:
  • onpaletteadd: (function) called when the node type is added to the palette.
    :point_up:
  • onpaletteremove: (function) called when the node type is removed from the palette.

If I remember correctly, it's triggered when the user dragged a node onto the workspace from the palette.

From my notes:

/** Available methods:
 * onadd: (function) Called when added to a flow (drag from palette, copy/paste, import) - WARN: If paste/import cancelled this is still fired.
 * oneditprepare: (function) called when the edit dialog is being built.
 * oneditsave:   (function) called when the edit Done button pressed - before save happens.
 * oneditcancel: (function) called when the edit Cancel button pressed - before cancel happens.
 * oneditdelete: (function) called when the delete button in a configuration node’s edit dialog is pressed - before delete.
 * oneditresize: (function) called when the edit dialog is resized.
 * onpaletteadd: (function) called when the node type is added to the palette.
 * onpaletteremove: (function) called when the node type is removed from the palette.
 */

I maintain a node-red-testbed repo where I do my experiments, you can see it on GitHub. Very useful when trying to work things like this out.

2 Likes

Thanks for this list Julian. and thanks all for chiming in above.

Interestingly I am finding that onpaletteadd is fired at every reload of the browser. So the below-posted coderesults in a new config node getting created for every reload of the editor.

I appreciate that literally the node type is being "added" to the palette at every reload however I read the docs as meaning that the function would be run once when the node was added to the palette-set on first installation via the palette manager. I wonder whether others are being misled by this ambiguity?

What's also interesting is that when running onpaletteadd the editor does not appear to have access to the eachConfig method of the RED.nodes api. but it is available after the page has loaded (or at some time before). the screenshot below should show that RED.nodes.eachConfig results in a couple of nodes that should have resulted in hasConfig = true; but in fact nothing is returned so the lambda function is never called.

onpaletteadd: function () {
            let hasConfig = false;
            RED.notify('palette add being run by hvac');
            console.log("palette add being run by hvac");
            RED.nodes.eachConfig((n) => {
                console.log("inside the each config");
                console.log(n);
                if (n.type === 'matter-hub') hasConfig = true;
                this.serverNode = n.id;
                $("#node-input-serverNode").val(n.id);
            });
            if (!hasConfig) {
                RED.notify('creating config');
                console.log("creating config");
                const configNode = {
                    id: RED.nodes.id(),
                    _def: RED.nodes.getType("matter-hub"),
                    type: "matter-hub",
                    users: []
                };
                RED.nodes.add(configNode);
                RED.nodes.dirty(true);
                this.serverNode = configNode.id;
                $("#node-input-serverNode").val(configNode.id);
            } else {
                RED.notify('not creating config');
            }
        },

Just flagging that I have worked around the issue for my own needs (by exporting a singleton that is used by the device nodes and wholly avoiding config nodes). The above may be useful for other's coming across confusion over onpaletteadd.

Using onadd would also have been an entirely valid solution as the registry has been fully loaded by then.

@TotallyInformation
I see there is also an onremove callback that can be used (there's a deprecation warning attached). Fired when the node is deleted from the flow.

and that the oneditdelete event appears to relate to all node types and not just the config node; and is fired when the node is deleted from the flow.

cf @node-red/editor-client/public/red/red.js around lines 5240 - 5257