I am working on a Node-RED flow where I have a ui-template
node rendering a network topology using Next.js. The topology is displayed when navigating to the /topo1
page. However, I am facing an issue when switching between tabs (e.g., page1
to topo1
or page2
to topo1
). Each time I navigate to /topo1
, the topology renders again below the previous instance, leading to multiple instances of the topology being displayed on the same page.
Here is a summary of my flow:
- A
ui-template
node is used to render the topology. - Navigation is handled using
ui-event
andswitch
nodes to detect the active tab and render the appropriate content.
Steps I have tried:
- I checked the flow logic and ensured the nodes are correctly wired.
- Verified the
ui-template
node's configuration, ensuring it is scoped to the page.
What I need help with:
- How can I prevent multiple instances of the topology from being rendered when switching to the
/topo1
tab? - Is there a way to clear or reset the
ui-template
content when switching tabs?
[
{
"id": "ff48d7ee859f6e3f",
"type": "debug",
"z": "a738176e45f8f666",
"name": "debug 2587",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 550,
"y": 200,
"wires": []
},
{
"id": "65960a3bd9b90826",
"type": "ui-template",
"z": "a738176e45f8f666",
"group": "",
"page": "2ca7ac6b39b2c442",
"ui": "",
"name": "topo_trial",
"order": 1,
"width": "10",
"height": "6",
"head": "",
"format": "<template>\n <h1>Topology</h1>\n <div id=\"topology-container\"></div>\n</template>\n<link rel=\"alternate icon\" href=\"./favicon.ico\" type=\"image/png\" sizes=\"16x16\">\n<!-- <link rel=\"stylesheet\" href=\"./nextjs/next.css\"> -->\n <link rel=\"stylesheet\" href=\"./assets/nextjs/next.css\">\n<script src=\"./assets/nextjs/next.js\"></script>\n<script>\n (function(nx){\nconst topologyData = {\nnodes: [\n// ISPs\n{ id: 0, x: 400, y: -100, name: \"ISP1\", device_type: \"cloud\", color: \"grey\" },\n{ id: 1, x: 600, y: -100, name: \"ISP2\", device_type: \"cloud\", color: \"grey\" },\n\n// Routers\n{ id: 2, x: 400, y: 0, name: \"Edge1\", device_type: \"router\", color: \"red\" },\n{ id: 3, x: 600, y: 0, name: \"Edge2\", device_type: \"router\", color: \"red\" },\n\n// Switches\n{ id: 4, x: 400, y: 100, name: \"Switch1\", device_type: \"switch\" },\n{ id: 5, x: 600, y: 100, name: \"Switch2\", device_type: \"switch\" },\n\n// Servers\n{ id: 6, x: 200, y: 200, name: \"ESX1\", device_type: \"server\" },\n{ id: 7, x: 400, y: 200, name: \"ESX2\", device_type: \"server\" },\n{ id: 8, x: 600, y: 200, name: \"ESX3\", device_type: \"server\" },\n{ id: 9, x: 800, y: 200, name: \"ESX4\", device_type: \"server\" },\n\n// SAN\n{ id: 10, x: 500, y: 300, name: \"SAN\", device_type: \"server\" }\n],\nlinks: [\n// WAN to routers\n{ source: 0, target: 2, color: \"green\" },\n{ source: 1, target: 3 },\n\n// Routers to switches\n{ source: 2, target: 4, color: \"green\" },\n{ source: 2, target: 5 },\n{ source: 3, target: 4 },\n{ source: 3, target: 5 },\n\n// Switches to Switches\n{ source: 4, target: 5 },\n{ source: 4, target: 5 },\n\n// Servers to Switches\n{ source: 6, target: 4, color: \"green\" },\n{ source: 6, target: 5, color: \"red\" },\n{ source: 7, target: 4, color: \"green\" },\n{ source: 7, target: 5, color: \"red\" },\n{ source: 8, target: 4, color: \"green\" },\n{ source: 8, target: 5, color: \"red\" },\n{ source: 9, target: 4, color: \"green\" },\n{ source: 9, target: 5, color: \"red\" },\n\n// SAN to Switches\n{ source: 10, target: 4, color: \"red\" },\n{ source: 10, target: 4, color: \"red\" },\n{ source: 10, target: 5, color: \"red\" },\n{ source: 10, target: 5, color: \"red\" }\n]\n};\n// instantiate next app\nconst app = new nx.ui.Application();\n\n// configuration object\nconst topologyConfig = {\n// configuration for nodes\nwidth: window.innerWidth,\nheight: window.innerHeight,\nnodeConfig: {\nlabel: \"model.name\",\niconType: \"model.device_type\",\ncolor: \"model.color\",\n},\n// configuration for links\nlinkConfig: {\nlinkType: \"straight\",\ncolor: \"model.color\"\n},\n// if true, the nodes' icons are shown, a dot is shown instead\nshowIcon: true,\n};\n\n// instantiate Topology class\nconst topology = new nx.graphic.Topology(topologyConfig);\n\n// load topology data from app/data.js\ntopology.data(topologyData);\n\n// bind the topology object to the app\ntopology.attach(app);\n\n// app must run inside a specific container. In our case this is the one with id=\"topology-container\"\napp.container(document.getElementById(\"topology-container\"));\n\n})(nx);\n</script>\n<style>\n /* define any styles here - supports raw CSS */\n</style>",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "widget:page",
"className": "nextjs",
"x": 380,
"y": 200,
"wires": [
[
"ff48d7ee859f6e3f"
]
]
},
{
"id": "4dfe62d884f54b12",
"type": "ui-event",
"z": "a738176e45f8f666",
"ui": "64fc71361e24a0d0",
"name": "",
"x": 110,
"y": 200,
"wires": [
[
"4710c8b6b890847b"
]
]
},
{
"id": "4710c8b6b890847b",
"type": "switch",
"z": "a738176e45f8f666",
"name": "",
"property": "payload.page.path",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "/topo1",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 230,
"y": 200,
"wires": [
[
"65960a3bd9b90826"
]
]
},
{
"id": "2ca7ac6b39b2c442",
"type": "ui-page",
"name": "topo1",
"ui": "64fc71361e24a0d0",
"path": "/topo1",
"icon": "home",
"layout": "grid",
"theme": "default",
"breakpoints": [
{
"name": "Default",
"px": "0",
"cols": "3"
},
{
"name": "Tablet",
"px": "576",
"cols": "6"
},
{
"name": "Small Desktop",
"px": "768",
"cols": "9"
},
{
"name": "Desktop",
"px": "1024",
"cols": "12"
}
],
"order": -1,
"className": "",
"visible": "true",
"disabled": "false"
},
{
"id": "64fc71361e24a0d0",
"type": "ui-base",
"name": "",
"path": "/dashboard",
"appIcon": "",
"includeClientData": true,
"acceptsClientConfig": [
"ui-iframe",
"ui-template",
"ui-gauge",
"ui-chart",
"ui-slider",
"ui-form",
"ui-text-input",
"ui-number-input",
"ui-file-input",
"ui-button",
"ui-button-group",
"ui-dropdown",
"ui-radio-group",
"ui-switch",
"ui-text",
"ui-chart",
"ui-form",
"ui-number-input",
"ui-switch",
"ui-table",
"ui-gauge",
"ui-markdown",
"ui-iframe",
"ui-tabulator",
"ui-radio-group",
"ui-dropdown",
"ui-button-group",
"ui-file-input",
"ui-notification",
"ui-control"
],
"showPathInSidebar": false,
"showPageTitle": false,
"navigationStyle": "icon",
"titleBarStyle": "fixed"
}
]