BestPractice to reuse Subflow and set paramters for specific instance?

Hi,

I wanted to create a function that gets

  • 1 dynamic parameter
  • 4 static parameters for each instance.

I found that a multi input function node is NOT wanted and why
I can fully understand and accept this!
(I do not want to bring this up again: Suggestion: A simple way to implement multiple input nodes :no_mouth: )

my subflow looks like this:

// set some Values in a UIBUILDER driven GUI

// define a basic default set - for the case garbage comes in
// so we can handle or debug better within the GUI
let myMsg = { topic: "Nd", payload: "Nd" }
let myNewPayload = "-";

// here we build the parameters we need in the GUI
// this values would be nice to define outside the subflow 
// so the (sub)flow has not to be copied around and changed
myNewPayload = { 
    "elementtype": "id", // could be id/class/attribute
    "elementselector": "ccc-hatch", // any CSS selector
    "todo": "set", // set/add/remove/toggle
    "targettype": "data-status", // innerHTML, any attribute e.g. data-* (more to come)
    "targetvalue": msg.payload // the value whatever you want to be
};

myMsg.topic = "updateElement: ccc-hatch";
myMsg.payload = myNewPayload;

return myMsg;

So what would be the fastest and cleanest way to set / define those 4 parameters for each instance.
(They never change for one instance - doing that with join and inject seems much to overloading for about 50 or even more instances)

Building my own node for that is on the plan - but not within 2024 :rofl: - just want to get it done

best regards
Antonio B.

If your subflow is simply a function node, you would greatly benefit from using the link-call node and making a simple subroutine.

Each subflow results in multiple nodes being generated every single node inside X the umber of instances. For example: if your subflow has 10 nodes and you have 50 instances, that is 500 nodes.
A Link-call subroutine equivalent would be 10 + 2

[{"id":"03caf84ad794a574","type":"inject","z":"eaa6d97a6ede80ab","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"seven","payload":"7","payloadType":"num","x":170,"y":2900,"wires":[["158d0f3af2d3e05a"]]},{"id":"529d2fd7dc616683","type":"inject","z":"eaa6d97a6ede80ab","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"nine","payload":"9","payloadType":"num","x":170,"y":2940,"wires":[["158d0f3af2d3e05a"]]},{"id":"158d0f3af2d3e05a","type":"link call","z":"eaa6d97a6ede80ab","name":"","links":["4057e7d7e66892ce"],"linkType":"static","timeout":"30","x":370,"y":2920,"wires":[["c943c9c28b42ec77"]]},{"id":"c943c9c28b42ec77","type":"debug","z":"eaa6d97a6ede80ab","name":"decorated payload","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":620,"y":2920,"wires":[]},{"id":"2e3f6c78987eb8d1","type":"group","z":"eaa6d97a6ede80ab","name":"My subroutine (much lighter than a subflow)","style":{"fill":"#92d04f","fill-opacity":"0.22","label":true},"nodes":["4057e7d7e66892ce","b5344392fd72a5ad","8c0101828c34f512"],"x":124,"y":2999,"w":312,"h":82},{"id":"4057e7d7e66892ce","type":"link in","z":"eaa6d97a6ede80ab","g":"2e3f6c78987eb8d1","name":"decorate-payload","links":[],"x":165,"y":3040,"wires":[["b5344392fd72a5ad"]]},{"id":"b5344392fd72a5ad","type":"function","z":"eaa6d97a6ede80ab","g":"2e3f6c78987eb8d1","name":"decorate","func":"// set some Values in a UIBUILDER driven GUI\n\n// define a basic default set - for the case garbage comes in\n// so we can handle or debug better within the GUI\n\nconst myMsg = RED.util.cloneMessage(msg);\n\n// here we build the parameters we need in the GUI\n// this values would be nice to define outside the subflow \n// so the (sub)flow has not to be copied around and changed\nconst myNewPayload = { \n    \"elementtype\": \"id\", // could be id/class/attribute\n    \"elementselector\": \"ccc-hatch\", // any CSS selector\n    \"todo\": \"set\", // set/add/remove/toggle\n    \"targettype\": \"data-status\", // innerHTML, any attribute e.g. data-* (more to come)\n    \"targetvalue\": msg.payload, // the value whatever you want to be\n    originalTopic: msg.topic\n};\n\nmyMsg.topic = \"updateElement: ccc-hatch\";\nmyMsg.payload = myNewPayload;\n\nreturn myMsg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":3040,"wires":[["8c0101828c34f512"]]},{"id":"8c0101828c34f512","type":"link out","z":"eaa6d97a6ede80ab","g":"2e3f6c78987eb8d1","name":"link out 1","mode":"return","links":[],"x":395,"y":3040,"wires":[]}]

some additional notes:

  1. your function disregards the original msg. This is not advised. One day, you will encounter an issue where the original msg has a property that is required downstream in the flow.
  2. you let create myNewPayload as a string then change it to an object. Better to simply const create the object const myNewPayload = { ... }
  3. you let create myMsg with a topic and payload, then overwrite them below. Better to const create the myMsg object e.g. const myMsg = {} then simply append final values `

Here is how I restructured your function


const myMsg = RED.util.cloneMessage(msg);

const myNewPayload = { 
    "elementtype": "id", // could be id/class/attribute
    "elementselector": "ccc-hatch", // any CSS selector
    "todo": "set", // set/add/remove/toggle
    "targettype": "data-status", // innerHTML, any attribute e.g. data-* (more to come)
    "targetvalue": msg.payload, // the value whatever you want to be
    originalTopic: msg.topic
};

myMsg.topic = "updateElement: ccc-hatch";
myMsg.payload = myNewPayload;

return myMsg;
2 Likes

Wow :melting_face:

Yes :face_with_open_eyes_and_hand_over_mouth: all my subflows are small functions like this.
Ups ... so all my small subflows should be changed ...

I will try to update and change those things as soon as possible to prevent using wrong habbits :scream_cat:.
I guess I will need some time to fully understand and get used to the correct usage.

  1. noticed *
  2. & 3. thought a const can not be changed any more *

* I will try to improve, remind myself and thank you very much for explaining all the stuff!

I will have to make a snapshot tonight :laughing: before touching this

best regards
Antonio B.

const means you cannot change the "thing" - i.e. a const obj = {} will always be an object, but you can change its properties.

Final note:

The function node is rather heavy for simple payload manipulation. when you ditch the subflows, try to change some of the smaller ones to change nodes.

also, it is important to give your link-in nodes good (and unique) names to make the best of subroutines (the link-in name appears in the link-call node)

OT: @Steve-Mcl, this is very interesting. So, in which case you are compelled to use Subflows?

no, the opposite.

Link-call is very good for PURE *1 (and stateless *2 ) functions & I recommend this style of programming first and foremost.

the issue I attempted to spell out is:

scenario (subflow):

  • subflow1 has 10 nodes
  • flows use 50 subflow instances
  • therefore, 500 nodes are programmatically instantiated in the core. Some of these may have init and teardown code - that is a lot of work.

scenario (subroutine):

  • subroutine has 10 nodes (plus the link-in + link-out (12 in total)
  • flows use 50 link-calls
  • therefore, only 12 nodes are programmatically instantiated in the core.


*1 A pure function is a function which: (a) Given the same input, will always return the same output and (b) Produces no side effects.

*2 stateful flows can be achieved with link-call by using named context for storage & retrieval, but subflows hide this complexity.

1 Like

Yes, I got this and maybe I misspelt my question.

Given the consolidated advice to use subroutines whenever possible, when you cannot avoid the use of subflows?

Not sure if there is really a question there, but to round it off, my personal recomendation is:

  • try to program without side-effects use link-call first.
  • if you need state (i.e. something is updated/remembered and that value can affect the next message or the flow output) then use regular flows UNLESS you need it more than once, then package it up into a subflow.
1 Like

Yes.
I have another question on subroutines, but I'll open a new thread, not to clutter this one.
Thanks

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.