Changing global variable inside the function node

Yes, it really is possible with 1 line of code! :smile:

All praise the bountiful wisdom of Nick and Dave who made that possible - obviously that isn't standard JavaScript, it is a core function in Node-RED.

The second syntax uses JavaScript's array subscript format. You can use that format instead of the dot notation, while generally discouraged, it is really useful if you end up with an object that contains properties who's names are not valid for the dot notation.

For example:

var fred = {"one": 1, "two.jimbob": 2}

While you can access the first property with fred.one, you cannot do that with the second property since the property name contains a dot. Instead you can use fred['two.jimbob'].

This is also useful when you want to use a variable to access a property:

var whome = 'one'
var fred = {"one": 1, "two.jimbob": 2}

console.log( fred[whome] )  // prints "1"

So, do you thing it is possible instead of:

var p = {};
p=msg.payload;
switch (msg.topic) {
case "obed03" : 
    var obed = global.get("obed03"); 
    obed.payload.POWER=p;
    obed.payload.enabled=true;
    global.set("obed03",obed);
    break;
.....

I can write:

switch (msg.topic) {
case "obed03":
    global.set("[msg.topic].payload.POWER", msg.payload);
    global.set("[msg.topic].payload.enabled", true);
    break;
......

Better to do:

switch (msg.topic) {
case "obed03":
    global.set(msg.topic + ".payload.POWER", msg.payload);
    global.set(msg.topic + ".payload.enabled", true);
    break;
......

Or even (assuming you are using Node.JS v8+) - note the backticks instead of double quotes:

switch (msg.topic) {
case "obed03":
    global.set(`${msg.topic}.payload.POWER`, msg.payload);
    global.set(`${msg.topic}.payload.enabled`, true);
    break;
......

Thanks. I will check.
BTW, this is exactly an example what is killing me while I am working with Node.JS/Node-Red.

Soooo many different syntaxes to achieve the same goal. How can I be sure, which syntax is the proper one, which syntax to use. Here I am really missing Python syntax purism and predictability.

Is there some manual of RECENT (Node.JSv8+??) syntax, please?
Otherwise, I am really LOST.

Thank you anyway

One more question to this topic.
What is the proper code inside the function node?

var msg3 = {};
a3=global.get("obed03");
msg3.payload=a3.payload.POWER;
msg3.enabled=a3.payload.enabled;
return msg3;

or directly:

var msg3 = {};
msg3.payload=global.get("obed03.payload.POWER");
msg3.enabled=global.get("obed03.payload.template");
return msg3;

Hmm, v2 vs v3? It is true that JS can have a few too many ways to do the same things - but each language has its own quirks.

For Node.js itself, simply check the Node.js website - it has an excellent manual. For JavaScript in general, the Mozilla Developer Network (MDN) is generally the best and most accurate source.

However, I always have to do a LOT of Googling (actually I mainly use DuckDuckGo these days to avoid tracking) since I'm not a full-time pro developer.

Then use a good IDE such as VScode where you can create your own snippets. VScode is actually built using Node.js and Electron and its native understanding of Node and npm packages is outstanding. Also install and configure JSlint for VScode as this will catch all of the little annoying errors before you get as far as running your code.

For function nodes, the built-in ACE editor is pretty good too. It will catch a load of issues for you so pay attention to the error and warning icons it gives you. Use the library feature to remember snippets of code for things that you struggle to remember. For example, I always struggle to remember the right syntax for JavaScript select/case statements. So saving a template one to the library means I can simply reproduce a sample when I need to.

Thank you for sharing your experience and for your suggestions.
Anyway, your help is really GREAT.!!!!!
My NR SELECT / CASE statement in my function node was 78 lines of code.
Now, thanks to your help, no more SELECT / CASE statement and 3 lines of code only!!! :smiley:

[{"id":"509784e2.ebf6fc","type":"function","z":"f2e2f228.998238","name":"CASE set recent ENERGY values to globals (obedXX)","func":"var total = msg.payload.Total;\nvar today = msg.payload.Today;\nvar yesterday =  msg.payload.Yesterday;\n\nswitch (msg.topic) {\ncase \"obed03\" : \n    var obed = global.get(\"obed03\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed03\",obed);\n    break;\ncase \"obed04\" : \n    var obed = global.get(\"obed04\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed04\",obed);\n    break;\ncase \"obed05\" : \n    var obed = global.get(\"obed05\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed05\",obed);\n    break;\ncase \"obed06\" : \n    var obed = global.get(\"obed06\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed06\",obed);\n    break;\ncase \"obed07\" :\n    var obed = global.get(\"obed07\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed07\",obed);\n    break;\ncase \"obed08\" : \n    var obed = global.get(\"obed08\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed08\",obed);\n    break;\ncase \"obed09\" : \n    var obed = global.get(\"obed09\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed09\",obed);\n    break;\ncase \"obed10\" : \n    var obed = global.get(\"obed10\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed10\",obed);\n    break;\ncase \"obed11\" : \n    var obed = global.get(\"obed11\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed11\",obed);\n    break;\ncase \"obed12\" : \n    var obed = global.get(\"obed12\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed12\",obed);\n    break;\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":1720,"y":1140,"wires":[[]]},{"id":"1e597e7a.9bd5ca","type":"function","z":"f2e2f228.998238","name":"NEW dynamic set recent ENERGY values to globals (obedXX)","func":"global.set(msg.topic + \".payload.Total\", msg.payload.Total);\nglobal.set(msg.topic + \".payload.Today\", msg.payload.Today);\nglobal.set(msg.topic + \".payload.Yesterday\", msg.payload.Yesterday);\n","outputs":1,"noerr":0,"x":1740,"y":1200,"wires":[[]]}]

I wonder, why it is not possible within a change node. Something like:
Screenshot%20from%202019-03-11%2000-02-23

You can probably do it with JSONata as it can also access global vars. I don't think that the global var setting is designed to work with variables but JSONata certainly is.

Very interesting thread indeed.

I could not understand why the final solution needs 3 global.set lines in the function node. Can´t it be reduced to one line of the kind?

global.set(msg.topic); // for testing only. Clear the the global property.

//global.set(msg.topic + ".payload.Total", msg.payload.Total);
//global.set(msg.topic + ".payload.Today", msg.payload.Today);
//global.set(msg.topic + ".payload.Yesterday", msg.payload.Yesterday);

global.set(msg.topic, {"payload" : msg.payload});
return msg;

It would not be a matter of optimization but making the code easier to understand. Using three lines seems to introduce unnecessary redundancy to the code.

Testing flow:

[{"id":"f05ed819.d29b38","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"4089bbcc.d1db34","type":"function","z":"f05ed819.d29b38","name":"NEW ","func":"global.set(msg.topic); // for testing only. Clear the the global property.\n\n//global.set(msg.topic + \".payload.Total\", msg.payload.Total);\n//global.set(msg.topic + \".payload.Today\", msg.payload.Today);\n//global.set(msg.topic + \".payload.Yesterday\", msg.payload.Yesterday);\n\nglobal.set(msg.topic, {\"payload\" : msg.payload});\nreturn msg;\n","outputs":1,"noerr":0,"x":510,"y":100,"wires":[["ade2fd71.cf45e"]]},{"id":"5f409428.4e3f1c","type":"change","z":"f05ed819.d29b38","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"obed1","tot":"str"},{"t":"set","p":"payload.Total","pt":"msg","to":"3","tot":"num"},{"t":"set","p":"payload.Today","pt":"msg","to":"2","tot":"num"},{"t":"set","p":"payload.Yesterday","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":100,"wires":[["4089bbcc.d1db34"]]},{"id":"8a723c98.cda02","type":"inject","z":"f05ed819.d29b38","name":"","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":100,"wires":[["5f409428.4e3f1c"]]},{"id":"ade2fd71.cf45e","type":"debug","z":"f05ed819.d29b38","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":630,"y":100,"wires":[]},{"id":"9b27cc43.fce5d","type":"function","z":"f05ed819.d29b38","name":"NEW ","func":"global.set(msg.topic); // for testing only. Clear the the global property.\n\nglobal.set(msg.topic + \".payload.Total\", msg.payload.Total);\nglobal.set(msg.topic + \".payload.Today\", msg.payload.Today);\nglobal.set(msg.topic + \".payload.Yesterday\", msg.payload.Yesterday);\n\n//global.set(msg.topic, msg.payload);\nreturn msg;\n","outputs":1,"noerr":0,"x":510,"y":160,"wires":[["194a52c3.21f5dd"]]},{"id":"e4be4713.86f5d8","type":"change","z":"f05ed819.d29b38","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"obed1","tot":"str"},{"t":"set","p":"payload.Total","pt":"msg","to":"3","tot":"num"},{"t":"set","p":"payload.Today","pt":"msg","to":"2","tot":"num"},{"t":"set","p":"payload.Yesterday","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":160,"wires":[["9b27cc43.fce5d"]]},{"id":"9ce989c7.439138","type":"inject","z":"f05ed819.d29b38","name":"","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":160,"wires":[["e4be4713.86f5d8"]]},{"id":"194a52c3.21f5dd","type":"debug","z":"f05ed819.d29b38","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":630,"y":160,"wires":[]}]

Hmmm. This was exactly the reason for my long nights sitting in front of the screen pulling my hair out :smiley:
I didn’t understand, the code you are suggesting,

global.set(msg.topic, {"payload" : msg.payload});

is overwriting (re-instantiating) a global variable COMPLETELY. It is not changing property values as I intended.
It means ALL other values stored in the global variable properties are LOST. See the example below:

[{"id":"cd8f21e1.31158","type":"function","z":"5085dcc5.5bf8dc","name":"set globals (obedXX)","func":"// rybarska mista bez chaticek\nvar r1='<left> <font size=\"4\">';\nvar r2='<img src=\"/verejne/icons/misc/man-fishing.png\" height=\"42\" width=\"42\"> </font></left>';\n\n// rybarska mista s chatickou\nvar c1='<left> <font size=\"4\">';\nvar c2='<img src=\"/verejne/icons/misc/NicePng_house-icon-png_147010.png\" height=\"36\" width=\"36\"> </font></left>';\n\n// rybarska mista bez chaticek\nglobal.set(\"obed03\",{payload:{template:r1+3+r2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed04\",{payload:{template:r1+4+r2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed05\",{payload:{template:r1+5+r2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed10\",{payload:{template:r1+1+r2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed11\",{payload:{template:r1+11+r2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\n\n// rybarska mista s chatickou\nglobal.set(\"obed06\",{payload:{template:c1+6+c2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed07\",{payload:{template:c1+7+c2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed08\",{payload:{template:c1+8+c2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed09\",{payload:{template:c1+9+c2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nglobal.set(\"obed12\",{payload:{template:c1+12+c2, Total:0, Today:0, Yesterday:0, POWER:0, enabled:false}});\nnode.warn(global.get(\"obed03\"));\nreturn msg;","outputs":1,"noerr":0,"x":340,"y":260,"wires":[["34a238ea.41b6b","c0b37f64.08f49","ff0c7997.cf8a7"]]},{"id":"5fa35193.735a4","type":"function","z":"5085dcc5.5bf8dc","name":"new dynamic","func":"\nglobal.set(msg.topic + \".payload.Total\", msg.payload.Total);\nglobal.set(msg.topic + \".payload.Today\", msg.payload.Today);\nglobal.set(msg.topic + \".payload.Yesterday\", msg.payload.Yesterday);\nnode.warn(global.get(msg.topic));\n\n\n","outputs":1,"noerr":0,"x":810,"y":300,"wires":[[]]},{"id":"b7327526.7024e8","type":"inject","z":"5085dcc5.5bf8dc","name":"","topic":"","payload":"{}","payloadType":"json","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":150,"y":260,"wires":[["cd8f21e1.31158"]]},{"id":"34a238ea.41b6b","type":"change","z":"5085dcc5.5bf8dc","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"obed04","tot":"str"},{"t":"set","p":"payload.Total","pt":"msg","to":"3","tot":"num"},{"t":"set","p":"payload.Today","pt":"msg","to":"2","tot":"num"},{"t":"set","p":"payload.Yesterday","pt":"msg","to":"1","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":300,"wires":[["5fa35193.735a4"]]},{"id":"c0b37f64.08f49","type":"change","z":"5085dcc5.5bf8dc","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"obed03","tot":"str"},{"t":"set","p":"payload.Total","pt":"msg","to":"400","tot":"num"},{"t":"set","p":"payload.Today","pt":"msg","to":"400","tot":"num"},{"t":"set","p":"payload.Yesterday","pt":"msg","to":"400","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":240,"wires":[["c6b857e2.37efa"]]},{"id":"c6b857e2.37efa","type":"function","z":"5085dcc5.5bf8dc","name":"old static original","func":"var total =300;\nvar today = 300;\nvar yesterday = 300;\nvar obed = global.get(\"obed03\"); \n    obed.payload.Total=total;\n    obed.payload.Today=today;\n    obed.payload.Yesterday=yesterday;\n    global.set(\"obed03\",obed);\nnode.warn(global.get(\"obed03\"));\n\n","outputs":1,"noerr":0,"x":820,"y":240,"wires":[[]]},{"id":"40719069.a4e8e","type":"function","z":"5085dcc5.5bf8dc","name":"Andrei","func":"global.set(msg.topic); // for testing only. Clear the the global property.\n\n//global.set(msg.topic + \".payload.Total\", msg.payload.Total);\n//global.set(msg.topic + \".payload.Today\", msg.payload.Today);\n//global.set(msg.topic + \".payload.Yesterday\", msg.payload.Yesterday);\n\nglobal.set(msg.topic, {\"payload\" : msg.payload});\n\nnode.warn(global.get(msg.topic));\nreturn msg;\n","outputs":1,"noerr":0,"x":790,"y":380,"wires":[[]]},{"id":"ff0c7997.cf8a7","type":"change","z":"5085dcc5.5bf8dc","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"obed05","tot":"str"},{"t":"set","p":"payload.Total","pt":"msg","to":"500","tot":"num"},{"t":"set","p":"payload.Today","pt":"msg","to":"500","tot":"num"},{"t":"set","p":"payload.Yesterday","pt":"msg","to":"500","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":380,"wires":[["40719069.a4e8e"]]}]
1 Like

Hi Petr, I (wrongly) assumed that there were no other properties in msg.payload so the code snippet I proposed will not work indeed. Your last flow clearly (and brilliantly) demonstrated that. I take the opportunity to thank you for opening this thread as I could learn something new about the global context.

Thank you Andrei.
Anyway, I feel I am lost in this new word of JavaScritp, mainly because of so many different syntax flavours ...
Indeed, the whole NR IDE looks to me kind of unfriendly for debugging purposes...
But I am trying to continue :smiley:
I will try the environment Julian Knight is using (see his post above).

Hi Petr,

Indeed it is possible to the functions flowContext and globalContextin a JSONata expression to get values from flow and global context. As far as I remember it is not possible to set values to the context via JSONata.

If you want to play with JSONata it is worth to read this enlightening thread.

I don't think it is possible to use a JSONata expression in the change node in the way you illustrated above.

Andrei, thanks for your interest.
I was looking already for JSONata solution/approach.
It looks to me far complicated, comparing to a function node easy approach. Because of that, I think not worth to investigate it deeper...

As a founder member of NUAGV [1] I have to point out that all this stress over setting variables can be avoided by not using global (or flow) context to store variables. Instead use messages to pass data to the places it is needed, which is what node-red is all about. There may possibly be a few occasions in which it is better to use context (for variable data) but I have so far not needed to use it.

[1] Node red Users Against Global (and flow) Variables. Any offers of a more snappy acronym gratefully received.

I see. Thank you for your advise, I am using the NR few weeks only, so this is all new to me.
I will investigate. Can you, please, give some point to resources where to get info and examples how to use NR messages?

The whole node red system is based on messages. That is what msg is referring to when you use msg.payload etc. When you join nodes up you are providing the wires along which the messages flow.

There’s the getting started part on the website https://nodered.org/docs/getting-started/first-flow

and several Node-RED tutorials online which you can find using google

I was not reading properly your post, sorry. I was thinking there is some mysterious message variable or so on, which I do not see. Sorry about my confusing reply.
I am in the situation, where I do need to keep the state of Sonoff/Tasmota switch (on/off, available/not available) and compare it with the incoming mqtt information. For this purposes, I am using global variables. Is this approach wrong? I can not imaging how to do it the other way, because flows are not connected.

Is not that state already available in mqtt from the Sonoff. Why do you need to store it locally too?