Hi everyone,
I'm working on a Vue.js project using Vuetify, and I have a v-combobox
that I want to display on all pages in my application. However, I only want to load and render the data for the combobox on specific pages where it's needed.
Here's a simplified version of my template:
<template>
<div>
<div class="msg">
<Teleport v-if="mounted" to="#app-bar-title">
<div style="order: 2;">
<div style="display: flex; justify-content: center;">
<v-combobox
label="Topology"
:items="topologies.map(topology => topology.name)"
variant="outlined"
style="width: 300px; height: 50px; margin-top: 60px; border-radius: 25px 25px 0 0;"
prepend-inner-icon="mdi-magnify"
v-model="selectedTopology"
></v-combobox>
</div>
</div>
</Teleport>
</div>
</div>
</template>
<script>
export default {
data() {
return {
mounted: false,
topologies: [],
selectedTopology: null,
};
},
mounted() {
this.mounted = true;
// Logic to load data for specific pages
},
};
</script>
and my flows given below
[
{
"id": "bbe5b8ba69b9e209",
"type": "function",
"z": "472eadd660afe821",
"name": "function 14",
"func": "const getTopologies = global.get('getTopologies'); // Retrieve the function\nif (!getTopologies) {\n node.error(\"getTopologies function not found in global context\");\n return null;\n}\n\n// Define an async wrapper function to handle the async call\n(async () => {\n try {\n // Call the getTopologies function\n const topologies = await getTopologies();\n console.log(\"Topologies data:\", topologies);\nnode.log(\"----------------->Path: \" + msg.payload.page.path);\n\n // Attach the topologies data to the message\n msg.payload = topologies;\n\n // Send the updated message\n node.send(msg);\n } catch (err) {\n // Handle any errors\n node.error(\"Error fetching topologies: \" + err.message);\n }\n})();\n\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 330,
"y": 200,
"wires": [
[
"2ccf9075586b5fa9"
]
]
},
{
"id": "bdeb805bf2151fa6",
"type": "debug",
"z": "472eadd660afe821",
"name": "debug 2585",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 670,
"y": 200,
"wires": []
},
{
"id": "2ccf9075586b5fa9",
"type": "ui-template",
"z": "472eadd660afe821",
"group": "",
"page": "dc3b0354cb683966",
"ui": "",
"name": "topo_trial",
"order": 1,
"width": "10",
"height": "6",
"head": "",
"format": "<template>\n <div>\n <div class=\"msg\">\n <Teleport v-if=\"mounted\" to=\"#app-bar-title\">\n <div style=\"order: 2;\">\n <div style=\"display: flex; justify-content: center;\">\n \n <v-combobox label=\"Topology\" :items=\"topologies.map(topology => topology.name)\"\n variant=\"outlined\" style=\"width: 300px; height: 50px; margin-top: 60px; border-radius: 25px 25px 0 0;\"\n prepend-inner-icon=\"mdi-magnify\" v-model=\"selectedTopology\"\n ></v-combobox>\n \n </div>\n \n \n </div>\n \n </Teleport>\n\n <div id=\"topology-container\"></div>\n </div>\n <link rel=\"alternate icon\" href=\"./favicon.ico\" type=\"image/png\" sizes=\"16x16\">\n <link rel=\"stylesheet\" href=\"./assets/nextjs/next.css\">\n </div>\n\n <!-- context menu -->\n <v-menu v-model=\"contextMenuVisible\" :style=\"{ left: `${menuX}px`, top: `${menuY}px`, position: 'absolute' }\">\n <v-list>\n <v-list-item v-for=\"(list, i) in lists\" :key=\"i\" @click=\"handleMenuClick(list.title)\">\n <v-list-item-title>{{ list.title }}</v-list-item-title>\n </v-list-item>\n </v-list>\n </v-menu>\n</template>\n\n<script src=\"./assets/nextjs/next.js\"></script>\n<script>\n export default {\n data() {\n return {\n mounted: false,\n topologies: [], // Array of topology objects\n selectedTopology: null,\n topologyApp: null,\n topologyInstance: null,\n value:[],\n labelText: \"\",\n dialogTitle: \"\",\n contextMenuVisible: false,\n menuX: 0,\n menuY: 0,\n lists: [\n { title: 'Item1' },\n { title: 'Item2' },\n ],\n };\n },\n watch: {\n selectedTopology(newValue) {\n console.log('Selected topology changed:', newValue);\n this.onTopologyChange(newValue);\n },\n },\n mounted() {\n this.mounted = true;\n // Listen for incoming messages to populate topologies\n this.$watch('msg.payload', (newPayload) => {\n if (Array.isArray(newPayload)) {\n this.topologies = newPayload;\n console.log(\"Topologies updated:\", this.topologies);\n }\n });\n document.addEventListener('contextmenu', (event) => {\n event.preventDefault();\n console.log(\"Click detected on:\", event.target);\n if (event.target.closest('.node')) {\n const node = event.target.closest('.node');\n const dataId = node.getAttribute('data-id');\n const labelElement = node.querySelector('.node-label');\n this.labelText = labelElement ? labelElement.textContent || labelElement.innerText : 'No label';\n \n // Store the position of the right-click\n this.menuX = event.clientX ;\n this.menuY = event.clientY ;\n \n // Open the context menu\n this.contextMenuVisible = true;\n console.log(\"Context menu should be visible now\");\n console.log(\"Position:\", this.menuX, this.menuY);\n \n console.log(\"Node data-id:\", dataId);\n console.log(\"Node label:\", this.labelText);\n }\n else {\n this.contextMenuVisible = false; // Hide the menu if not clicking on a node\n }\n });\n },\n methods: {\n selectTopology(topology) {\n console.log(\"Selected topology:\", this.selectedTopology);\n this.selectedTopology = topology.name;\n this.changeTopology(topology.data);\n },\n onTopologyChange(selected) {\n console.log(\"Topology selected in combobox:\", selected); // Added log\n const topology = this.topologies.find(topology => topology.name === selected);\n if (topology) {\n console.log(\"Topology found:\", topology); // Added log\n this.selectTopology(topology);\n }else {\n console.error(\"Selected topology not found in list\"); // Added log\n }\n },\n initializeTopology(topologyData) {\n console.log(\"Initializing topology:\", topologyData);\n\n if (!this.topologyApp) {\n const app = new nx.ui.Application();\n const topologyConfig = {\n width: window.innerWidth,\n height: window.innerHeight,\n nodeConfig: {\n label: \"model.name\",\n iconType: \"model.device_type\",\n color: \"model.color\",\n },\n linkConfig: {\n linkType: \"straight\",\n color: \"model.color\",\n },\n showIcon: true,\n };\n\n const topology = new nx.graphic.Topology(topologyConfig);\n topology.attach(app);\n app.container(document.getElementById(\"topology-container\"));\n // Attach the app to the element with class 'nrdb-layout-overlay'\n const parentElement = document.querySelector('#topology-container');\n console.log(\"Parent element:\", parentElement);\n const container = parentElement?.querySelector('.nrdb-layout-group--grid');\n console.log(\"Container element:\", container);\n const topo = document.querySelector('.n-topology-blue');\n if (topo) {\n parentElement.appendChild(topo);\n }\n this.topologyApp = app;\n this.topologyInstance = topology;\n\n // // Adding event listeners for expand/collapse\n // topology.on('expandAll', this.expandAllNodes);\n // topology.on('collapseAll', this.collapseAllNodes);\n }\n\n this.topologyInstance.data(topologyData);\n },\n // expandAllNodes() {\n // this.topologyInstance.expandAll(); // Ensure this method is defined in your topology library\n // },\n // collapseAllNodes() {\n // this.topologyInstance.collapseAll(); // Ensure this method is defined in your topology library\n // },\n changeTopology(topologyData) {\n console.log(\"Changing topology to:\", topologyData);\n\n if (this.topologyInstance) {\n console.log(\"Updating existing topology instance\"); // Added log\n this.topologyInstance.data(topologyData);\n } else {\n console.log(\"Topology instance not found, initializing new topology\"); // Added log\n this.initializeTopology(topologyData);\n }\n },\n },\n unmounted() {\n if (this.topologyApp) {\n this.topologyApp.destroy();\n }\n },\n};\n</script>\n\n<style>\n #topology-container {\n width: 100%;\n height: 100%;\n /* background-color: #f0f0f0; */\n overflow: hidden;\n }\n\n body {\n background-color: white;\n }\n\n .v-application__wrap {\n min-height: unset !important;\n }\n\n #app-bar-title {\n overflow: visible;\n position: relative;\n /* display:block; */\n /* z-index: 1000; */\n \n }\n\n /* v-combobox {\n display: block !important; \n } */\n\n .v-menu {\n position: absolute !important;\n z-index: 9999;\n }\n \n\n.bg-primary {\n background-color: #795548 !important;\n color: white !important;\n}\n/* .v-btn {\n height: 48px;\n margin-top: 60px;\n} */\n</style>\n\n\n",
"storeOutMessages": true,
"passthru": true,
"resendOnRefresh": true,
"templateScope": "widget:page",
"className": "nextjs",
"x": 500,
"y": 200,
"wires": [
[
"bdeb805bf2151fa6"
]
]
},
{
"id": "8ce6855e818820d5",
"type": "ui-event",
"z": "472eadd660afe821",
"ui": "64fc71361e24a0d0",
"name": "",
"x": 50,
"y": 200,
"wires": [
[
"072452c884b36f30"
]
]
},
{
"id": "072452c884b36f30",
"type": "switch",
"z": "472eadd660afe821",
"name": "",
"property": "payload.page.path",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "/topology",
"vt": "str"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 170,
"y": 200,
"wires": [
[
"bbe5b8ba69b9e209"
]
]
},
{
"id": "dc3b0354cb683966",
"type": "ui-page",
"name": "topology",
"ui": "64fc71361e24a0d0",
"path": "/topology",
"icon": "home",
"layout": "grid",
"theme": "",
"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-control",
"ui-template",
"ui-gauge",
"ui-chart",
"ui-slider",
"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-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-form"
],
"showPathInSidebar": false,
"showPageTitle": false,
"navigationStyle": "icon",
"titleBarStyle": "fixed"
}
]
I want the combobox to be part of the navigation bar, so it's visible on all pages, but the topologies
data should only be loaded and rendered when a specific set of pages is visited.
Could anyone advise on the best way to achieve this? Is there a pattern or approach in Vue.js or Vuetify that facilitates this type of behavior?
Thanks in advance for your help!