如何基于JSON文件在Node-RED中动态生成分组内Switch节点流?
问题描述
我需要基于JSON配置文件,在Node-RED的单个UI分组内创建Switch节点流。每个Switch的名称、标签、payload值由JSON定义,通过msg.payload填充。尝试用Function节点调用含单个ui-switch的子流,但循环只生成了JSON里的最后一个开关设备。
JSON配置示例(实际最多支持20个设备)
{ "devices": [ { "name": "device1", "label": "sw1", "value": true }, { "name": "device2", "label": "sw2", "value": false }, { "name": "device3", "label": "sw3", "value": true } ] }
测试流代码
[ { "id": "d0f133cb5a7b79e2", "type": "subflow", "name": "DynamicNode", "info": "", "category": "", "in": [ { "x": 520, "y": 640, "wires": [ { "id": "3890f4d5b7c9afd3" }, { "id": "0fecbc6a798d86f5" } ] } ], "out": [], "env": [], "meta": {}, "color": "#DDAA99" }, { "id": "3890f4d5b7c9afd3", "type": "ui_text", "z": "d0f133cb5a7b79e2", "group": "bcfd32ebf2a31e62", "order": 3, "width": 0, "height": 0, "name": "", "label": "{{\"msg.label\":\"msg.label\"}}", "format": "{{\"msg.text\":\"msg.text\"}}", "layout": "row-spread", "className": "", "style": false, "font": "", "fontSize": 16, "color": "#000000", "x": 610, "y": 540, "wires": [] }, { "id": "0fecbc6a798d86f5", "type": "ui_switch", "z": "d0f133cb5a7b79e2", "name": "", "label": "{{msg.label}}", "tooltip": "", "group": "bcfd32ebf2a31e62", "order": 4, "width": 0, "height": 0, "passthru": true, "decouple": "false", "topic": "topic", "topicType": "msg", "style": "", "onvalue": "{\"msg.switch\":true}", "onvalueType": "json", "onicon": "", "oncolor": "", "offvalue": "{\"msg.switch\":false}", "offvalueType": "json", "officon": "", "offcolor": "", "animate": false, "className": "", "x": 790, "y": 580, "wires": [ [] ] }, { "id": "bcfd32ebf2a31e62", "type": "ui_group", "name": "test", "tab": "4ef3afefcfd15d15", "order": 1, "disp": true, "width": "6", "collapse": false, "className": "" }, { "id": "4ef3afefcfd15d15", "type": "ui_tab", "name": "test", "icon": "question", "disabled": false, "hidden": false }, { "id": "35c906478f19015b", "type": "tab", "label": "test", "disabled": false, "info": "", "env": [] }, { "id": "inject", "type": "inject", "z": "35c906478f19015b", "name": "", "props": [ { "p": "payload" } ], "repeat": "", "crontab": "", "once": true, "onceDelay": 0.1, "topic": "", "payload": "", "payloadType": "date", "x": 570, "y": 460, "wires": [ [ "functionNode" ] ] }, { "id": "functionNode", "type": "function", "z": "35c906478f19015b", "name": "Generate Subflow Instances", "func": "let jsonString = '[{\"text\": \"Text1\", \"switch\": true}, {\"text\": \"Text2\", \"switch\": false}, {\"text\": \"Text3\", \"switch\": true}]';\nlet definitions = JSON.parse(jsonString);\nlet outputMsgs = [];\nfor (let i = 0; i < definitions.length; i++) {\n let msg = {\n label: `Item ${i+1}`,\n text: definitions[i].text,\n switch: definitions[i].switch\n };\n outputMsgs.push(msg);\n}\nreturn [outputMsgs];", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", "libs": [], "x": 840, "y": 460, "wires": [ [ "dynamicNodeSubflow" ] ] }, { "id": "dynamicNodeSubflow", "type": "subflow:d0f133cb5a7b79e2", "z": "35c906478f19015b", "name": "DynamicNodeSubflow", "x": 1140, "y": 460, "wires": [] } ]
预期效果
实现手动创建的堆叠式开关列表布局,每个开关带有独立标签(对应房间音频音量调节位置),开关按配置顺序垂直排列。
解决方案
问题根源
当前子流内的UI节点(ui_text、ui_switch)使用固定order值(3和4),所有消息会覆盖同一UI节点的配置,最终只显示最后一条数据的控件。同时Function节点输出的消息数组直接传入子流,子流无法区分处理每条独立消息。
修复步骤
1. 改造子流,支持动态排序
- 打开子流编辑界面,添加环境变量:名称
baseOrder,类型数字,默认值1 - 修改子流内
ui_text节点:order设为${env.baseOrder}- 标签改为
{{msg.label}},格式改为{{msg.name}}(修正原模板语法错误)
- 修改子流内
ui_switch节点:order设为${env.baseOrder} + 1onvalue改为true,offvalue改为false(无需包装成JSON,直接传递布尔值)topic设为{{msg.name}},方便后续区分不同设备的开关事件
2. 更新Function节点,生成带唯一排序的消息数组
替换Function节点内代码为:
// 可替换为读取外部JSON文件或从msg.payload获取配置 const deviceConfig = { "devices": [ { "name": "device1", "label": "sw1", "value": true }, { "name": "device2", "label": "sw2", "value": false }, { "name": "device3", "label": "sw3", "value": true } ] }; // 为每个设备分配唯一排序值,避免UI节点重叠 const msgArray = deviceConfig.devices.map((device, index) => { return { payload: device.value, name: device.name, label: device.label, baseOrder: index * 2 + 1 // 间隔2,给文本和开关各留位置 }; }); return [msgArray];
3. 添加Split节点拆分消息数组
在Function节点和子流之间插入Split节点,设置为拆分消息数组,确保每个子流实例接收单独的配置消息,避免覆盖。
最终流结构
Inject → Function(生成配置消息数组) → Split → 子流(带动态排序的UI节点)
内容的提问来源于stack exchange,提问作者Harvey Mushman
相关产品推荐
相关产品推荐

