Hi @sverre,
I had overlooked your question, due to the large amount of posts that are created nowadays on Discourse. But it was a very good question!
Last night I was also wondering how I needed to do it, so I found your post. I have asked Paul to reopen this discussion, so I can share my solution. Perhaps you don't need it anymore, but then others might benefit from it ...
The original node
In your original node you have a property with a default value:
<script type="text/javascript">
RED.nodes.registerType('my-node',{
defaults: {
originalProperty: {value: "defaultValue"}
},
...
});
</script>
That property is displayed on the node's config screen:
<script type="text/x-red" data-template-name="my-node">
...
<div class="form-row">
<label for="node-input-originalProperty">Original property</span></label>
<input type="number" id="node-input-originalProperty">
</div>
...
</script>
And the entered property value becomes available on the server side after a deploy:
module.exports = function(RED) {
var settings = RED.settings;
function MyNode(config) {
RED.nodes.createNode(this, config);
this.originalProperty = config.originalProperty;
...
}
RED.nodes.registerType("my-node", MyNode);
}
So far so good. But as soon as lots of these nodes are added to a flow, people need to start copying the value of originalProperty to all those nodes to keep them in sync. Which is rather annoying...
The updated node
So you want to introduce a config node, so users can enter their value in that config node (and re-use it in multiple nodes). But you need to migrate the original value into the config node, otherwise people will loose their original property values (because the original input field won't be visible anymore).
-
Create a config node (i.e. html and js file) which contains now the originalProperty:
<script type="text/javascript">
RED.nodes.registerType('my-config-node',{
category: "config",
defaults: {
originalProperty: {value: "defaultValue"}
},
...
});
</script>
And the original property is now displayed on the config node' config screen:
<script type="text/x-red" data-template-name="my-node">
...
<div class="form-row">
<label for="node-input-config-originalProperty">Original property</span></label>
<input type="number" id="node-input-config-originalProperty">
</div>
...
</script>
-
Remove the original html input element "node-input-originalProperty" because the original property shouldn't be visualized anymore on the node's config screen. Instead we will add a new input field, that will be used as a placeholder where Node-RED can show a dropdown with all available config nodes:
<script type="text/x-red" data-template-name="my-node">
...
<div class="form-row">
<label for="node-input-myConfig">Configuration</span></label>
<input type="number" id="node-input-myConfig">
</div>
...
</script>
-
Since the original property values need to be migrated, make sure that those values are still being remembered by Node-Red by keeping the originalProperty under the cover:
<script type="text/javascript">
RED.nodes.registerType('my-node',{
defaults: {
originalProperty: {value: null},
myConfig: {value:"", type: "my-config-node"},
},
...
});
</script>
Note that we use a default value of null
for the original value, to be able afterwards to determine whether we have an original value (that needs to be migrated).
-
As soon as the config screen of our original node is opened, a new config node needs to be instantiated (when there is no config node yet and there are original values to be migrated):
oneditprepare: function() {
var node = this;
// Migration of old nodes when no config node is available, and the original property is available
if(!node.myConfig && node.originalProperty) {
var configNodeId = RED.nodes.id();
var configNodeName = "some config name";
// Create a new config node
node.myConfig = {
id: configNodeId,
_def: RED.nodes.getType("my-config-node"),
type: "my-config-node",
hasUsers: false,
users: [],
name: configNodeName,
label: function() { return this.name || "some name"}
}
// Copy the value of the original property to the config node
node.myConfig.originalProperty = node.originalProperty;
node.originalProperty = null;
// Make sure Node-RED knows about the new config node instance (so you can deploy it)
RED.nodes.add(node.myConfig);
// Add the new config node in the dropdown and make sure it selected.
// Otherwise it would only be displayed in the list, the next time you open this config screen...
$("#node-input-myConfig").append($('<option>', {
value: configNodeId,
text: configNodeName,
selected: "selected"
}));
}
-
Update the server side (js file) to use the originalProperty from the config node. Or if there is no config node yet, use the originalProperty which is still available in the node (which will be the case if the user has not opened the node's config screen yet):
module.exports = function(RED) {
var settings = RED.settings;
function MyNode(config) {
RED.nodes.createNode(this, config);
// Retrieve the config node, where the timings are configured
var myConfigNode = RED.nodes.getNode(config.myConfig);
if (myConfigNode) {
// Use the original property value from the config node
node.originalProperty = myConfigNode.originalProperty;
}
else {
// Use the original property value from the original node
node.originalProperty = config.originalProperty;
}
...
}
RED.nodes.registerType("my-node", MyNode);
}
This way a new config node is automatically created when you open the node's config screen, where the original property value is displayed. And when a new node is dragged onto the flow, no new config node is created automatically: indeed the user perhaps want to re-use an existing config node...
Good luck!
Bart