How to Display a Vuetify Combobox Across All Pages but Load Data on Specific Pages?

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!

Architecturally speaking, I think you've nailed it. I don't have a chance to dive into your flow at the moment, but using:

UI Event > Switch > Template (with a Teleport)

would be exactly how I'd achieve this.

sorry i m not get it.can you please guide me .