You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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} + 1
    • onvalue改为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 14:04:54