The problem
To make the life better, and to make flows more robust, with a single centralized update, I make use of configuration/options constants.
But where to place configuration data?
Environement
- pros: standard. Environment variables can be used as subflow parameters.
- cons: complex installation. To update you must exit node-red and do some oprations (O.S. drpendant).
Not for all user, only valid for few and constant values (e.g. username), not for user options.
Global context
- pros: easy to set and update inside node-red, e.g. using the node-contrib-config, very friendly.
- cons: based on the current subflow implementation, parameters cannot come from the global context.
my solution
Until the subflow implementation does not change, I have found a simple workaround: a node-function that does a late binding (at runtime) of some/all parameters of a subflow, so we can implement an indirect parameter definition and use the node-contrib-config to manage the options.
features
late binding:
-
if the subflow has a parameter whose value (string) start with:
global.
flow.
msg.
this function calculates the actual value and assigns it to the parameter.
runtime update:
-
if the incoming
msg
containsmsg.<paramName>:tempvalue
thentempvalue
is temporarily assigned to parameter (optional). -
the dynamic value can be obtained inside the subflow using
flow.get(<paramName>)
(instead ofenv.get(<paramName>
), still valid for static parameters).
use
In your subflow:
- Put a 'late blinding' function node between the msg input and your nodes.
- Update the function code, adding at end one line for any dynamic parameter:
_upadateParam(<dynamicName1>);
_upadateParam(<dynamicName2>);
- If you are updating an existing subflow, replace all
env.get(<dynamicNameX>
withflow.get(<dynamicNameX>
.
Enjoy
[
{
"id": "faaa7cdb933cb272",
"type": "subflow",
"name": "late binding example",
"info": "Subflows and configuration: late and runtime parameters binding.\n\n## The problem\nTo make the life better, and to make flows more robust, with a single centralized update, I make use of configuration/options constants.\n\nBut where to place configuration data?\n\n### Environement\n- **pros:** standard. Environment variables can be used as subflow parameters.\n- **cons:** complex installation. To update you must exit node-red and do some opration (O.S. drpendant).\n\nNot for all user, only valid for few and constant values (e.g. username), not for user options.\n\n### Global context\n- **pros:** easy to set and update inside node-red, e.g. using the **node-contrib-config**, very friendly.\n- **cons:** based on the current subflow [implementation](https://nodered.org/docs/user-guide/environment-variables), parameters cannot come from the global context.\n\n## my solution\nUntil the subflow implementation does [not change](https://discourse.nodered.org/t/jsonata-in-subflow-interface/59813?u=msillano), I have found a simple workaround: a node-function that does a late binding (at runtime) of some/all parameters of a subflow, so we can implement an indirect parameter definition and use the **node-contrib-config** to manage the options.\n\n## features\n**late binding:**\n 1. if the subflow has a parameter whose value (string) start with:\n - `global.`\n - `flow.`\n - `msg.`\n this function calculates the actual value and assigns it to the parameter.\n\n**runtime update:**\n 2. if the incoming `msg` contains `msg.<paramName>:tempvalue`\n then `tempvalue` is temporarily assigned to parameter (optional).\n\n 3. the dynamic value can be obtained inside the subflow using `flow.get(<paramName>)` (instead of `env.get(<paramName>`), still valid for static parameters).\n \n## use\n In your subflow:\n 1. Put a 'late bimding' function node between the msg input and your nodes.\n 2. Update the function code, adding at end one line for any dynamic parameter:\n```` \n _upadateParam(<dynamicName1>); \n _upadateParam(<dynamicName2>); \n```` \n 3. If you are updating an existing subflow, replace all `env.get(<dynamicNameX>` with `flow.get(<dynamicNameX>`.\n\nEnjoy\n",
"category": "",
"in": [
{
"x": 60,
"y": 60,
"wires": [
{
"id": "323fcee7d2ae40cd"
}
]
}
],
"out": [],
"env": [
{
"name": "mypameter",
"type": "str",
"value": ""
}
],
"meta": {},
"color": "#DDAA99"
},
{
"id": "323fcee7d2ae40cd",
"type": "function",
"z": "faaa7cdb933cb272",
"name": "late binding",
"func": "// This function updates a parameter value and save it in a flow variable with the same name.\n// The nodes in the subflow, instead of using env.get() must use flow.get().\n// Call _upadateParam(<parameter_name>) for any parameter thath requires late or runtime binding.\n\nfunction _upadateParam(_pname){\n var _xvalue = env.get(_pname);\n if (_xvalue){\n _xvalue = _xvalue.toString().trim();\n if (_xvalue.startsWith('global.'))\n _xvalue = global.get(_xvalue.substring(7));\n else if(_xvalue.startsWith('flow.'))\n _xvalue = flow.get(\"$parent.\"+ _xvalue.substring(5));\n else if(_xvalue.startsWith('msg.'))\n _xvalue = msg[_xvalue.substring(4)];\n else _xvalue = env.get(_pname);\n }\n// optional: to set the parameter dynamically with a message property, same name\n if(msg[_pname])\n _xvalue = msg[_pname];\n \n// save actual value in the (sub)flow context \n flow.set(_pname, _xvalue);\n }\n \n// here CUSTOMIZE: \n_upadateParam(\"myparameter\") ; // the parameter name\n\nreturn msg;",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 190,
"y": 60,
"wires": [
[
"a81b3db0f226a117"
]
]
},
{
"id": "90e9b0da29850be2",
"type": "comment",
"z": "faaa7cdb933cb272",
"name": "here more subflow nodes",
"info": "",
"x": 410,
"y": 120,
"wires": []
},
{
"id": "a81b3db0f226a117",
"type": "debug",
"z": "faaa7cdb933cb272",
"name": "only for test",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "auto",
"x": 390,
"y": 60,
"wires": []
}
]