diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json index cbfdca9cd..7c13d0c5b 100755 --- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json @@ -277,6 +277,10 @@ "deleteSubflow": "delete subflow", "info": "Description", "category": "Category", + "env": { + "restore": "Restore to subflow default", + "remove": "Remove environment variable" + }, "errors": { "noNodesSelected": "Cannot create subflow: no nodes selected", "multipleInputsToSelection": "Cannot create subflow: multiple inputs to selection" diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 7678c8ca8..55ffbc022 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -550,54 +550,129 @@ RED.editor = (function() { .editableList({ addItem: function(container, i, opt) { var row = $('
').appendTo(container); - var nameField = $('', { - class: "node-input-env-name", - type: "text", - style: "margin-left: 5px; width: calc(40% - 5px)", - placeholder: RED._("common.label.name") - }).appendTo(row); + if (opt.parent) { + $('
', { + class:"uneditable-input", + style: "margin-left: 5px; width: calc(40% - 8px)", + }).appendTo(row).text(opt.name); + } else { + $('', { + class: "node-input-env-name", + type: "text", + style: "margin-left: 5px; width: calc(40% - 8px)", + placeholder: RED._("common.label.name") + }).appendTo(row).val(opt.name); + } var valueField = $('',{ class: "node-input-env-value", type: "text", - style: "margin-left: 5px; width: calc(60% - 5px)" + style: "margin-left: 5px; width: calc(60% - 8px)" }).appendTo(row) + valueField.typedInput({default:'str', - types:['str','num','bool','json','bin','re','date'] + types:['str','num','bool','json','bin','env'] }); - if (opt) { - nameField.val(opt.name); - valueField.typedInput('type', opt.type); - valueField.typedInput('value', opt.value); + + valueField.typedInput('type', opt.parent?(opt.type||opt.parent.type):opt.type); + valueField.typedInput('value', opt.parent?(opt.value||opt.parent.value):opt.value); + + var actionButton = $('',{href:"#",class:"red-ui-editableList-item-remove editor-button editor-button-small"}).appendTo(container); + $('',{class:"fa "+(opt.parent?"fa-reply":"fa-remove")}).appendTo(actionButton); + container.parent().addClass("red-ui-editableList-item-removable"); + if (opt.parent) { + if (opt.value && (opt.value !== opt.parent.value || opt.type !== opt.parent.type)) { + actionButton.show(); + } else { + actionButton.hide(); + } + var restoreTip = RED.popover.tooltip(actionButton,RED._("subflow.env.restore")); + valueField.change(function(evt) { + var newType = valueField.typedInput('type'); + var newValue = valueField.typedInput('value'); + if (newType === opt.parent.type && newValue === opt.parent.value) { + actionButton.hide(); + } else { + actionButton.show(); + } + }) + actionButton.click(function(evt) { + evt.preventDefault(); + restoreTip.close(); + valueField.typedInput('type', opt.parent.type); + valueField.typedInput('value', opt.parent.value); + }) + } else { + var removeTip = RED.popover.tooltip(actionButton,RED._("subflow.env.remove")); + actionButton.click(function(evt) { + evt.preventDefault(); + removeTip.close(); + container.parent().addClass("red-ui-editableList-item-deleting") + container.fadeOut(300, function() { + env_container.editableList('removeItem',opt); + }); + }); } }, - sortable: true, - removable: true + sortable: false, + removable: false }); - var envs = node.env; - if (envs) { - for (var i = 0; i < envs.length; i++) { - var env = envs[i]; - env_container.editableList('addItem', env); + var parentEnv = {}; + var envList = []; + if (/^subflow:/.test(node.type)) { + var subflowDef = RED.nodes.subflow(node.type.substring(8)); + if (subflowDef.env) { + subflowDef.env.forEach(function(env) { + var item = { + name:env.name, + parent: { + type: env.type, + value: env.value + } + } + envList.push(item); + parentEnv[env.name] = item; + }) } } + + if (node.env) { + for (var i = 0; i < node.env.length; i++) { + var env = node.env[i]; + if (parentEnv.hasOwnProperty(env.name)) { + parentEnv[env.name].type = env.type; + parentEnv[env.name].value = env.value; + } else { + envList.push({ + name: env.name, + type: env.type, + value: env.value + }); + } + } + } + envList.forEach(function(env) { + env_container.editableList('addItem', env); + }) } - function convEnv(editable_list) { - if (editable_list) { + function exportEnvList(list) { + if (list) { var env = []; - editable_list.each(function(i) { + list.each(function(i) { var entry = $(this); - var nf = entry.find(".node-input-env-name"); - var vf = entry.find(".node-input-env-value"); - var name = nf.val(); - var value = vf.typedInput("value"); - var type = vf.typedInput("type"); - var item = { - name: name, - type: type, - value: value - }; - env.push(item); + var item = entry.data('data'); + var name = item.parent?item.name:entry.find(".node-input-env-name").val(); + var valueInput = entry.find(".node-input-env-value"); + var value = valueInput.typedInput("value"); + var type = valueInput.typedInput("type"); + if (!item.parent || (item.parent.value !== value || item.parent.type !== type)) { + var item = { + name: name, + type: type, + value: value + }; + env.push(item); + } }); return env; } @@ -1343,7 +1418,7 @@ RED.editor = (function() { if (type === "subflow") { var old_env = editing_node.env; - var new_env = convEnv($("#node-input-env-container").editableList("items")); + var new_env = exportEnvList($("#node-input-env-container").editableList("items")); if (!isSameEnv(old_env, new_env)) { editing_node.env = new_env; changes.env = editing_node.env; @@ -2070,7 +2145,7 @@ RED.editor = (function() { } var old_env = editing_node.env; - var new_env = convEnv($("#node-input-env-container").editableList("items")); + var new_env = exportEnvList($("#node-input-env-container").editableList("items")); if (!isSameEnv(old_env, new_env)) { editing_node.env = new_env; changes.env = editing_node.env; diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js index 580c66df4..6bb8256c2 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/flows/Subflow.js @@ -265,7 +265,7 @@ class Subflow extends Flow { if (env && env.hasOwnProperty(name)) { var val = env[name]; try { - var ret = redUtil.evaluateNodeProperty(val.value, val.type, null, null, null); + var ret = redUtil.evaluateNodeProperty(val.value, val.type, this.node, null, null); return ret; } catch (e) { diff --git a/test/nodes/subflow/subflow_spec.js b/test/nodes/subflow/subflow_spec.js index 43fdd57d5..01c49c75a 100644 --- a/test/nodes/subflow/subflow_spec.js +++ b/test/nodes/subflow/subflow_spec.js @@ -166,7 +166,7 @@ describe('subflow', function() { n1.receive({payload:"foo"}); }); }); - + it('should access env var of subflow instance', function(done) { var flow = [ {id:"t0", type:"tab", label:"", disabled:false, info:""}, @@ -256,13 +256,11 @@ describe('subflow', function() { {id:"t0", type:"tab", label:"", disabled:false, info:""}, {id:"n1", x:10, y:10, z:"t0", type:"subflow:s1", env: [ - {name: "KS", type: "str", value: "STR"}, {name: "KN", type: "num", value: "100"}, {name: "KB", type: "bool", value: "true"}, {name: "KJ", type: "json", value: "[1,2,3]"}, {name: "Kb", type: "bin", value: "[65,65]"}, - {name: "KR", type: "re", value: "[A-Z]"}, - {name: "KD", type: "date", value: ""} + {name: "Ke", type: "env", value: "KS"} ], wires:[["n2"]]}, {id:"n2", x:10, y:10, z:"t0", type:"helper", wires:[]}, @@ -275,10 +273,13 @@ describe('subflow', function() { out:[{ x:10, y:10, wires:[ {id:"s1-n1", port:0} ] - }] + }], + env: [ + {name: "KS", type: "str", value: "STR"} + ] }, {id:"s1-n1", x:10, y:10, z:"s1", type:"function", - func:"msg.VS = env.get('KS'); msg.VN = env.get('KN'); msg.VB = env.get('KB'); msg.VJ = env.get('KJ'); msg.Vb = env.get('Kb'); msg.VR = env.get('KR'); msg.VD = env.get('KD'); return msg;", + func:"msg.VE = env.get('Ke'); msg.VS = env.get('KS'); msg.VN = env.get('KN'); msg.VB = env.get('KB'); msg.VJ = env.get('KJ'); msg.Vb = env.get('Kb'); return msg;", wires:[]} ]; helper.load(functionNode, flow, function() { @@ -292,14 +293,10 @@ describe('subflow', function() { msg.should.have.property("VJ", [1,2,3]); msg.should.have.property("Vb"); should.ok(msg.Vb instanceof Buffer); - msg.should.have.property("VR"); - should.ok(msg.VR instanceof RegExp); - msg.should.have.property("VD"); - should.ok((typeof msg.VD) === "number"); + msg.should.have.property("VE","STR"); done(); } catch (e) { - console.log(e); done(e); } });