Sub-flows. I am missing something in how they work

The same seems valid for flow.get/set (just modify Steves time example and try). But changing it to global you will see they will operate on the same variable that is not kept inside each instance of the
subflows, i.e. it is instead in the global context

(Idiot hat on)

Sorry. I'm not getting what you mean.

To break it down to the node in the subflow, this is what I have - NOT working:

node.warn("Message received");  // for the sake of knowing the message arrived
var x = flow.get($parent.test);
msg.payload = x;
return msg;

Though I think line 2 is the problem.

So the flow context (from the "parent" flow) I want to get is called test.

I did it as explained from Nick (other thread a while ago), but I am (obviously) missing something.

For the sake of my learning can you show me what that code needs to be?

What they are trying to say is that if you EXPLICITLY name the variable/context (GLobal or Flow) then each time the subflow runs it will only access that named instance - regardless of where you call it from - this would be desireable if the subflow was not called concurrently by many flows.

On the other hand if you were to call it concurrently from many flows then you will want each instance of the subflow variables to be instantiated and unique to that calling iteration until the parent process dies - otherwise they will be trampling all over each other

Craig

I appreciate the reply Craig, but I am still not getting it.

I have (again keeping it in line with what is already said) in the flow I set a flow variable test to something. (String)

At any time the sub-flow is called and needs to get the value of test from the parent flow.

But I am still not seeing a working way of doing this.

I think the brain is really not working just now. :frowning:

Just to be clearer, that is something I did not respond to, I do not know how you can grab flow data from the parent. Only thing I noticed during my tests is that you can get global data in the same way as usual but context and flow data stays local in the sub flow

No problems. I'm not saying you had to answer.

I'm just confused that I have read how to do it, but what is written doesn't work, or: I am doing something wrong.

Just looking for what the correct way of doing it is.

Not sure where this comes from, but I can imagine that a subflow is not part of the flow it is living in (it's in its' own little world), in terms of context.

Instead of trying to read the flow variable in the subflow, why not just inject it into the subflow node with a payload property ? You have to inject payloads anyway.

I see Nick is replying, but just to set the records straight:

Where I got the information

1 Like

Assuming to you want to access the flow context variable called test in the flow containing your subflow instance node, then

var x = flow.get($parent.test);

Should be

var x = flow.get("$parent.test")
3 Likes

You need quotes around the text otherwise it thinks $parent2.test is a local variable it needs to evaluate and already not the value of a variable.

[{"id":"a11d54.726bd2b","type":"subflow","name":"Subflow 1","info":"","in":[{"x":60,"y":80,"wires":[{"id":"63e10fae.b1efd"}]}],"out":[{"x":320,"y":80,"wires":[{"id":"63e10fae.b1efd","port":0}]}],"status":{"x":320,"y":160,"wires":[{"id":"63e10fae.b1efd","port":0}]}},{"id":"63e10fae.b1efd","type":"function","z":"a11d54.726bd2b","name":"","func":"node.warn(\"Message received\");  // for the sake of knowing the message arrived\nmsg.payload = flow.get(\"$parent.test\");\nreturn msg;","outputs":1,"noerr":0,"x":190,"y":80,"wires":[[]]},{"id":"37170f03.e7cad","type":"inject","z":"5051cb93.cf0d34","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":560,"wires":[["d1b2515d.89157"]]},{"id":"d1b2515d.89157","type":"change","z":"5051cb93.cf0d34","name":"","rules":[{"t":"set","p":"test","pt":"flow","to":"From parent 1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":250,"y":560,"wires":[["ea05925a.2f409"]]},{"id":"ea05925a.2f409","type":"subflow:a11d54.726bd2b","z":"5051cb93.cf0d34","name":"","env":[],"x":380,"y":620,"wires":[["6b0da688.4a4178"]]},{"id":"6b0da688.4a4178","type":"debug","z":"5051cb93.cf0d34","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":530,"y":620,"wires":[]},{"id":"e5896147.211da","type":"change","z":"5051cb93.cf0d34","name":"","rules":[{"t":"set","p":"test","pt":"flow","to":"From parent 2","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":250,"y":700,"wires":[["ea05925a.2f409"]]},{"id":"8d4e3fd3.745dc","type":"inject","z":"5051cb93.cf0d34","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":90,"y":700,"wires":[["e5896147.211da"]]}]

Thanks @knolleary and @dceejay.

With that resolved, how would I do what was shown in this real line:
var debug = flow.get(device+'_Button_Debug') || false;

device is resolved earlier from a local context.get( ).

My resolution to it is:
var debug = flow.get("$parent.device"+'_Button_Debug' ) || false;

correct?

I notice that how to use flow context in a subflow is documented in
https://nodered.org/docs/user-guide/context
but not in
https://nodered.org/docs/user-guide/editor/workspace/subflows

1 Like

I think

var debug = flow.get(`$parent.${device}_Button_Debug`) || false;

or

var debug = flow.get("$parent." + device + "_Button_Debug") || false;

if you are a Luddite.

That will result in you accessing the flow context variable $parent.device_Button_Debug

But I think you mean to get the value of $parent.device then add _Button_Debug to the end of that and use that as the key.

So if you already have device as a local variable containing the result of flow.get("$parent.device") then

var debug = flow.get(device+'_Button_Debug')

Would be correct.

If not, then I haven't understood what you want.

Either myself or @knolleary could be right. It depends what is in device.

I don't fully understand that. I thought that to get the value from the parent flow context the string defining the name had to start with "$parent." so that would only work if device contained a string "$parent.something"

I have interpreted the question as wanting to get the name of the device from parent context and use that as part of the key name when accessing local context.

I may well be wrong in that interpretation.

Sorry for the confusion.

Thanks for clearing up the confusion with the structure on building the name.

Just to reiterate the problem:

At the beginning a messages is sent to the sub-flow which sets the device name.
That is stored in sub-flow context( ) for that node.
Things happen and the device name is used to check/isolate/identify who is using the sub-flow.

Above the sub-flow I set a flow variable which indicates if the sub-flow has its debug active.
Obviously it is a flow variable as it needs to be accessed from the sub-flow.

When a message arrives in the sub-flow it checks if the flow variable (from the parent flow) is set to debug.

So I need to construct the name of the variable needed to check if the debug is active.
As in the flow/code I need to construct that name.

I need the device name with '_Button_Debug' for clarity.

So:

Correct. That is what I want.

So as an example:
The device is TimePi.
It needs to see if the flow variable named TimePi_Button_Debug is active. (True)

The original code is:
var debug = flow.get(device+'_Button_Debug') || false; // if active: true

That is what you (@knolleary) posted.

I am surprised that I got it correct.

Maybe I should have tested it first, before asking, but in light of other tests, it didn't seem to work.

Here is the flow I was using to establish if what I wrote is/was correct.
It doesn't work.

Walk through of this test flow:
Two inject nodes which set/change flow variable test to ONE or TWO.

The Test node invokes the sub-flow.
That reads the flow value of test and sends it out as the message.

Nothing comes out.

So I am confused why it should work, yet in testing, it doesn't seem to.

Flow here:

[{"id":"d03cee5c.debbf","type":"subflow","name":"Subflow 2","info":"","in":[{"x":220,"y":100,"wires":[{"id":"b6a05627.03a81"}]}],"out":[{"x":560,"y":100,"wires":[{"id":"b6a05627.03a81","port":0},{"id":"c320b7e7.d22ce","port":0}]}],"status":{"x":570,"y":190,"wires":[{"id":"16c3d7fc.e478","port":0}]}},{"id":"b6a05627.03a81","type":"function","z":"d03cee5c.debbf","name":"Get flow variable","func":"node.status({text:\"Message received\"});\nvar x;\nx = flow.get(test);\nmsg.payload = x;\nreturn msg;","outputs":1,"noerr":0,"x":400,"y":100,"wires":[[]]},{"id":"16c3d7fc.e478","type":"status","z":"d03cee5c.debbf","name":"","scope":null,"x":380,"y":190,"wires":[[]]},{"id":"c320b7e7.d22ce","type":"change","z":"d03cee5c.debbf","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$test","tot":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":50,"wires":[[]]},{"id":"9ea5acd.d1ad25","type":"debug","z":"accbdb61.d75918","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":580,"y":760,"wires":[]},{"id":"69a06ac9.813d24","type":"inject","z":"accbdb61.d75918","name":"Test","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":760,"wires":[["f6961980.dfd6d"]]},{"id":"f6961980.dfd6d","type":"subflow:d03cee5c.debbf","z":"accbdb61.d75918","name":"","x":340,"y":760,"wires":[["9ea5acd.d1ad25"]]},{"id":"26a16337.fdde04","type":"inject","z":"accbdb61.d75918","name":"One","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":620,"wires":[["37c662d1.dc05ae"]]},{"id":"37c662d1.dc05ae","type":"change","z":"accbdb61.d75918","name":"","rules":[{"t":"set","p":"test","pt":"flow","to":"ONE","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":620,"wires":[[]]},{"id":"f43ae422.a4c","type":"change","z":"accbdb61.d75918","name":"","rules":[{"t":"set","p":"test","pt":"flow","to":"TWO","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":670,"wires":[[]]},{"id":"79938c3d.d2cf7c","type":"inject","z":"accbdb61.d75918","name":"Two","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":670,"wires":[["f43ae422.a4c"]]}]

Sub flow:

[{"id":"b6a05627.03a81","type":"function","z":"d03cee5c.debbf","name":"Get flow variable","func":"node.status({text:\"Message received\"});\nvar x;\nx = flow.get(test);\nmsg.payload = x;\nreturn msg;","outputs":1,"noerr":0,"x":400,"y":100,"wires":[[]]},{"id":"16c3d7fc.e478","type":"status","z":"d03cee5c.debbf","name":"","scope":null,"x":380,"y":190,"wires":[[]]}]

What am I missing?
(It will be something obvious or silly. But hey, I'm sorry. I can't see it.)

Ok, I got the test flow working.

The function node is this:
(bit messy, but it has debug stuff)

node.status({text:"Message received"});
var x;
var y = "blank";
node.status({text:"blah1"});
x = flow.get("$parent.test");
y = '>>>' + x + '<<<';
node.status({text:"blah2"});
msg.payload = y;
return msg;

Great. Progress.

But the bigger problem is that the name (x) needs to be preceded by something. (the device in the real example).

So for the sake of it I'll make it A_

I'll set the A_ in the code. That's not a problem.

How do I attach it to the name of the parent flow variable name I get?

Does this help with the clarity?
(or make it worse?)

Update:
This is now the code in the function node.
Given that the parent flow has a flow variable A_test set to ONE:

node.status({text:"Message received"});
var prefix = 'A_';
var x;
var y = "blank";
var name = prefix + "test";
node.warn("Name set to " + name);
node.status({text:"blah1"});
x = flow.get("$parent.name");
y = '>>>' + x + '<<<';
node.status({text:"blah2"});
msg.payload = y;
return msg;

>>>undefined<<<

Stuck.

Oh, the node.warn works and I see A_test as the name.

Another update:

Modifying the code further to this:

node.status({text:"Message received"});
var prefix = 'A_';
var x;
var t;
var y = "blank";
var name = prefix + "test";
node.warn("Name set to " + name);
node.status({text:"blah1"});
x = flow.get("$parent.name");
t = flow.get("$parent.A_test");
node.warn("T is " + t);
y = '>>>' + x + '<<<';
node.status({text:"blah2"});
msg.payload = y;
return msg;

I get a correct reply/printout of T.
So there seems a problem with the name construction of x.

Making the line:
x = flow.get("$parent."+name);

resolves the problem.

At last!

Thanks all.

And in the bigger picture it should be this:
var debug = flow.get("$parent."+device+'_Button_Debug') || false; // if active: true

yes?