Need help with template node

I have created a template node with tabs and I have integrated a froms inside each tab, but my forms are getting a lot and if I keep adding them to the same template node the code will become a ridiculously large. My question is:

Is there a way to have a multiple template node with the forms and some how use a API or teleport or some other method make them appear in the tabs so I can separate this code little bit. I am starting to loose track of which code was for that form and inside witch tab and all of that

So if there is a method of separating that code little bit it will be helpful

Thank you

Are the forms identical?

You could use the new dialog group type to reuse the form on any page.

Is there a reason why you're using the Template, and not using the "Tab" layout type, with the in built form widgets?

Tabs: Layout: Tabs | Node-RED Dashboard 2.0

Forms: Form ui-form | Node-RED Dashboard 2.0

Yes, it's much more flexible and customizable! The built-in tabs aren't very attractive :smile::smile::smile:, no offense... and they feel too basic for my taste :smile::smile::smile:. The Main template is beautifully designed, easy to work with, and eye-catching — I really love that template node. It's flexible, but my question is how to split the code into different template nodes so they can work together as one. I know you all spent a lot of time building Dashboard 2, but there should be a way to split the code. Hopefully!!!

No they are completely different with different functions connected to a different function nodes

There are multiple ways of doing this. One example is to break each tab up into its own template node and combine them in the end.

example flow:

[{"id":"c793913fdbe6282e","type":"template","z":"19f6d93a528a7cbd","name":"tab 1","field":"tab1","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>\n    tab 1\n</div>","output":"str","x":330,"y":340,"wires":[["3efb506152ecb101"]]},{"id":"568551a787a090b6","type":"inject","z":"19f6d93a528a7cbd","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":340,"wires":[["c793913fdbe6282e"]]},{"id":"e94e8d68651686e1","type":"debug","z":"19f6d93a528a7cbd","name":"debug 654","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":670,"y":440,"wires":[]},{"id":"3efb506152ecb101","type":"template","z":"19f6d93a528a7cbd","name":"tab 2","field":"tab2","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>\n    tab 2\n</div>","output":"str","x":330,"y":400,"wires":[["ffbccf7d4090687b"]]},{"id":"87642c406cb2093a","type":"template","z":"19f6d93a528a7cbd","name":"combine","field":"payload","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"{{{tab1}}}\n{{{tab2}}}\n{{{tab3}}}","output":"str","x":520,"y":440,"wires":[["e94e8d68651686e1"]]},{"id":"ffbccf7d4090687b","type":"template","z":"19f6d93a528a7cbd","name":"tab 3","field":"tab3","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"<div>\n    tab 3\n</div>","output":"str","x":330,"y":460,"wires":[["87642c406cb2093a"]]}]

Note that the 'combine' template uses triple curly braces {{{ }}} to disable encoding.

Actually not this template I mean Dashboard 2 Template node!!!

It is hard a bit to maybe see whole picture but if I understand it at least slightly - here is one example how to combine smaller bits into one master template using the teleport

[{"id":"50165225957c11fa","type":"ui-template","z":"50af7128c761b2cb","group":"f4672a59c3c79c7f","page":"","ui":"","name":"master template","order":4,"width":"6","height":"4","head":"","format":"<template>\n<div class=\"master-slot-wrapper\">\n    <div id=\"slot_1\"></div>\n    <div id=\"slot_2\"></div>\n    <div id=\"slot_3\"></div>\n    <div id=\"slot_4\"></div>\n</div>\n    \n</template>\n\n<script>\n    export default {\n        data() {\n            return {\n              \n            }\n        },\n        watch: {\n           \n        },\n        computed: {\n           \n        },\n        methods: {\n          \n        },\n        mounted() {\n            // send event to dedicated event node to notify that master template is mounted\n            // at this point teleporting to here is possible\n            this.$socket.emit('ui-event', 'e152c365e8f8f0e1', {payload:\"master-template-mounted\"})\n        },\n        unmounted() {\n           \n        }\n    }\n</script>\n<style>\n    .master-slot-wrapper{\n        display:grid;\n        grid-template-columns:1fr 1fr;\n        grid-template-rows:1fr 1fr;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":860,"y":840,"wires":[[]]},{"id":"e152c365e8f8f0e1","type":"ui-event","z":"50af7128c761b2cb","ui":"cab2761fcbe60245","name":"","x":650,"y":700,"wires":[["1908688521f4ed6d","63d2ea23fda602f5","a0ba30fc5b0beeb8","6a13996bf1ffe12a","5acbadfac769a8e1"]]},{"id":"1908688521f4ed6d","type":"debug","z":"50af7128c761b2cb","name":"debug 11","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":840,"y":600,"wires":[]},{"id":"63d2ea23fda602f5","type":"ui-template","z":"50af7128c761b2cb","group":"f4672a59c3c79c7f","page":"","ui":"","name":"teleportable 1","order":5,"width":0,"height":0,"head":"","format":"<template>\n    <Teleport v-if=\"masterMounted && selfMounted\" to=\".master-slot-wrapper #slot_1\">\n        Template 1 content\n    </Teleport>\n</template>\n\n<script>\n    export default {\n        data() {\n            return {\n                masterMounted:false,\n                selfMounted:false               \n            }\n        },\n        watch: {\n           msg: function () {\n               if(this.msg?.payload == \"master-template-mounted\"){\n                    this.masterMounted = true \n               }\n            }\n        },\n        \n        mounted() {\n            this.selfMounted = true\n        },\n        unmounted() {\n           \n        }\n    }\n</script>\n<style>\n\n    .teleported-template {\n        display:none !important;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"teleported-template","x":860,"y":660,"wires":[[]]},{"id":"a0ba30fc5b0beeb8","type":"ui-template","z":"50af7128c761b2cb","group":"f4672a59c3c79c7f","page":"","ui":"","name":"teleportable 2","order":1,"width":0,"height":0,"head":"","format":"<template>\n    <Teleport v-if=\"masterMounted && selfMounted\" to=\".master-slot-wrapper #slot_2\">\n        Template 2 content\n    </Teleport>\n</template>\n\n<script>\n    export default {\n        data() {\n            return {\n                masterMounted:false,\n                selfMounted:false               \n            }\n        },\n        watch: {\n           msg: function () {\n               if(this.msg?.payload == \"master-template-mounted\"){\n                    this.masterMounted = true \n               }\n            }\n        },\n        \n        mounted() {\n            this.selfMounted = true\n        },\n        unmounted() {\n           \n        }\n    }\n</script>\n<style>\n\n    .teleported-template {\n        display:none !important;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"teleported-template","x":860,"y":700,"wires":[[]]},{"id":"6a13996bf1ffe12a","type":"ui-template","z":"50af7128c761b2cb","group":"f4672a59c3c79c7f","page":"","ui":"","name":"teleportable 3","order":2,"width":0,"height":0,"head":"","format":"<template>\n    <Teleport v-if=\"masterMounted && selfMounted\" to=\".master-slot-wrapper #slot_3\">\n        Template 3 content\n    </Teleport>\n</template>\n\n<script>\n    export default {\n        data() {\n            return {\n                masterMounted:false,\n                selfMounted:false               \n            }\n        },\n        watch: {\n           msg: function () {\n               if(this.msg?.payload == \"master-template-mounted\"){\n                    this.masterMounted = true \n               }\n            }\n        },\n        \n        mounted() {\n            this.selfMounted = true\n        },\n        unmounted() {\n           \n        }\n    }\n</script>\n<style>\n\n    .teleported-template {\n        display:none !important;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"teleported-template","x":860,"y":740,"wires":[[]]},{"id":"5acbadfac769a8e1","type":"ui-template","z":"50af7128c761b2cb","group":"f4672a59c3c79c7f","page":"","ui":"","name":"teleportable 4","order":3,"width":0,"height":0,"head":"","format":"<template>\n    <Teleport v-if=\"masterMounted && selfMounted\" to=\".master-slot-wrapper #slot_4\">\n        Template 4 content\n    </Teleport>\n</template>\n\n<script>\n    export default {\n        data() {\n            return {\n                masterMounted:false,\n                selfMounted:false               \n            }\n        },\n        watch: {\n           msg: function () {\n               if(this.msg?.payload == \"master-template-mounted\"){\n                    this.masterMounted = true \n               }\n            }\n        },\n        \n        mounted() {\n            this.selfMounted = true\n        },\n        unmounted() {\n           \n        }\n    }\n</script>\n<style>\n\n    .teleported-template {\n        display:none !important;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"local","className":"teleported-template","x":860,"y":780,"wires":[[]]},{"id":"f4672a59c3c79c7f","type":"ui-group","name":"Master","page":"b55c3e3640901741","width":"6","height":"1","order":1,"showTitle":true,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"cab2761fcbe60245","type":"ui-base","name":"My Dashboard","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control","ui-template"],"showPathInSidebar":false,"showPageTitle":false,"navigationStyle":"temporary","titleBarStyle":"fixed"},{"id":"b55c3e3640901741","type":"ui-page","name":"Test Page","ui":"cab2761fcbe60245","path":"/test","icon":"home","layout":"grid","theme":"0819910104699eae","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":4,"className":"","visible":"true","disabled":"false"},{"id":"0819910104699eae","type":"ui-theme","name":"Site Dark","colors":{"surface":"#141414","primary":"#32a00a","bgPage":"#121212","groupBg":"#141414","groupOutline":"#333333"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"12px","widgetGap":"12px"}}]
1 Like

I think I am missing something here can't get it to work

[{"id":"eb406c1bf6250141","type":"debug","z":"d29a438140be5717","name":"debug 656","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":530,"y":340,"wires":[]},{"id":"8ff25e3069e9bbac","type":"ui-event","z":"d29a438140be5717","ui":"cb79bc4520925e32","name":"","x":310,"y":340,"wires":[["660824168d458a91","eb406c1bf6250141"]]},{"id":"890e6be487685a2b","type":"ui-template","z":"d29a438140be5717","group":"","page":"c466b7e4bb472f99","ui":"","name":"ALL TABS ONLY","order":0,"width":0,"height":0,"head":"","format":"<template>\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Pompiere&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n  <v-app>\n    <v-main>\n      <v-card class=\"v-card-tab\">\n        <v-card-title class=\"text-center justify-center py-6\">\n          <!-- Future Space for Content -->\n          <h1 class=\"v-card-title-tap-h1\"></h1>\n        </v-card-title>\n\n        <v-tabs v-model=\"tab\" class=\"v-tabs-style\" grow>\n          <v-tab v-for=\"item in items\" :key=\"item\" :text=\"item\" :value=\"item\"></v-tab>\n        </v-tabs>\n\n        <v-tabs-window v-model=\"tab\">\n          <v-tabs-window-item v-for=\"item in items\" :key=\"item\" :value=\"item\">\n            <v-card color=\"basil\" flat>\n              <v-card-text>\n                <!-- ADD Trade Tab -->\n                <div v-if=\"tab === 'ADD'\" class=\"d-flex justify-center mb-10 div-style\">\n\t\t\t\t\t<div id=\"slot_1\">\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n                <!-- IMPORT Trade Tab -->\n                <div v-if=\"tab === 'IMPORT'\" class=\"d-flex justify-center mb-10 div-style\">\n\t\t\t\t</div>\n\n                <!-- SEARCH Trade Tab -->\n                <div v-if=\"tab === 'SEARCH'\" class=\"d-flex justify-center mb-10 div-style\">\n                </div>\n\n                <!-- MOVE Trade Tab -->\n\t\t\t\t<div v-if=\"tab === 'MOVE'\" class=\"d-flex justify-center mb-10 div-style\">\n\t\t\t\t</div>\n\n                <!-- CHANGE Trade Tab -->\n\t\t\t\t<div v-if=\"tab === 'CHANGE'\" class=\"d-flex justify-center mb-10 div-style\">\n\t\t\t\t</div>\n\n                <!-- REMOVE Trade Tab -->\n                <div v-if=\"tab === 'REMOVE'\" class=\"d-flex justify-center mb-10 div-style\">\n\t\t\t\t</div>\n\n                <!-- ADDS Tab -->\n                <div v-if=\"tab === 'ADDS'\" class=\"d-flex justify-center mb-10 div-style\">\n                </div>\n\t\t\t\t\n\t\t\t\t<div v-else>\n                  <p>Select a tab to view the data.</p>\n                </div>\n              </v-card-text>\n            </v-card>\n          </v-tabs-window-item>\n        </v-tabs-window>\n      </v-card>\n    </v-main>\n  </v-app>\n</template>\n\n<script>\nexport default {\n  data: () => ({\n    tab: 'ADD', // Default selected tab\n    items: ['ADD', 'IMPORT', 'SEARCH', 'MOVE', 'CHANGE', 'REMOVE', 'ADDS'],\n  }),\n\n  methods: {\n    // Add methods here when needed\n  },\n\n  mounted() {\n    // Send event to dedicated event node to notify that master template is mounted\n    // At this point, teleporting to here is possible\n    this.$socket.emit('ui-event', 'e152c365e8f8f0e1', { payload: \"master-template-mounted\" });\n  },\n\n  unmounted() {\n    // Add unmounted logic if needed\n  },\n};\n</script>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":330,"y":180,"wires":[[]]},{"id":"660824168d458a91","type":"ui-template","z":"d29a438140be5717","group":"","page":"c466b7e4bb472f99","ui":"","name":"ADD TRADE TAB","order":0,"width":0,"height":0,"head":"","format":"<template>\n\t<Teleport v-if=\"masterMounted && selfMounted\" to=\".master-slot-wrapper #slot_1\">\n\t  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Pompiere&display=swap\">\n\t  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n\t  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n\t  <v-app>\n\t\t<v-main>\n\t\t  <!-- ADD Trade Tab -->\n\t\t  <div class=\"d-flex justify-center mb-10 div-tab-style\">\n\t\t\t<v-sheet class=\"order-1 pa-2 ma-4 justify-center v-sheet-tab-style\">\n\t\t\t  <v-card class=\"mx-auto elevation-20 v-card-tab-style\" style=\"max-width: 800px;\">\n\t\t\t\t<v-toolbar class=\"v-toolbar-tab-style\" color=\"deep-purple-accent-4\" height=\"120\" cards dark flat>\n\t\t\t\t  <div class=\"v-toolbar-tab-div-text-style\">Add New Trade</div>\n\t\t\t\t</v-toolbar>\n\t\t\t\t<v-form ref=\"formAddTrade\" v-model=\"formAddTrade\" class=\"v-form-tab-style\">\n\t\t\t\t  <v-responsive class=\"mx-auto\" style=\"width: 450px;\">\n\t\t\t\t\t<v-textarea \n\t\t\t\t\t  v-model=\"addTradeSignal\" \n\t\t\t\t\t  auto-grow \n\t\t\t\t\t  box \n\t\t\t\t\t  color=\"deep-purple\" \n\t\t\t\t\t  rows=\"6\" \n\t\t\t\t\t  clearable \n\t\t\t\t\t  @input=\"validateAddTradeSignal\"\n\t\t\t\t\t  :error-messages=\"addTradeSignalError\"\n\t\t\t\t\t></v-textarea>\n\t\t\t\t  </v-responsive>\n\t\t\t\t</v-form>\n\t\t\t\t<v-divider :thickness=\"2\" class=\"border-opacity-100\" color=\"deep-purple accent-4\"></v-divider>\n\t\t\t\t<v-card-actions>\n\t\t\t\t  <v-btn \n\t\t\t\t\tsize=\"x-large\" \n\t\t\t\t\tblock \n\t\t\t\t\t:disabled=\"!isValidForm || addTradeSignal === ''\" \n\t\t\t\t\t:loading=\"isLoading\" \n\t\t\t\t\tclass=\"white--text\" \n\t\t\t\t\tcolor=\"deep-purple accent-4\" \n\t\t\t\t\t@click=\"sendTradeSignal\" \n\t\t\t\t\tdepressed\n\t\t\t\t  >\n\t\t\t\t\tAdd Trade\n\t\t\t\t  </v-btn>\n\t\t\t\t</v-card-actions>\n\t\t\t  </v-card>\n\t\t\t</v-sheet>\n\t\t  </div>\n\t\t</v-main>\n\t  </v-app>\n\t</Teleport>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      addTradeSignal: '', // For the \"Add Trade\" textarea content\n      addTradeSignalError: '', // Error message for invalid input\n      isLoading: false,\n      masterMounted: false,\n      selfMounted: false,\n    };\n  },\n  watch: {\n    msg: function () {\n      if (this.msg?.payload == \"master-template-mounted\") {\n        this.masterMounted = true;\n      }\n    },\n  },\n  computed: {\n    // Computed property to check if the form is valid\n    isValidForm() {\n      return this.addTradeSignalError === '';\n    },\n  },\n  methods: {\n    // Method to validate the \"Add Trade\" signal format\n    validateAddTradeSignal() {\n      const patternTradePair = /^[A-Za-z0-9]+\/[A-Za-z0-9]+$/;\n      const patternLeverage = /^Leverage Cross \\d+x$/;\n      const patternBuyPrice = /^Buy \\d+(\\.\\d+)?$/;\n      const patternSellPrice = /^Sell \\d+(\\.\\d+)?$/;\n      const patternTradeNumber = /^TRADE #\\d+$/;\n\n      const lines = this.addTradeSignal.split('\n').map(line => line.trim());\n\n      // If the input does not have exactly 6 lines, invalid\n      if (lines.length !== 6) {\n        this.addTradeSignalError = 'Signal must contain exactly 6 lines.';\n        return;\n      }\n\n      // Check if all the required patterns match the input format\n      const isValid =\n        patternTradePair.test(lines[0]) &&\n        patternLeverage.test(lines[1]) &&\n        patternBuyPrice.test(lines[2]) &&\n        patternSellPrice.test(lines[3]) &&\n        lines[4] === '' &&\n        patternTradeNumber.test(lines[5]);\n\n      // If invalid format, set error message, else clear it\n      if (isValid) {\n        this.addTradeSignalError = ''; // Clear error if valid\n      } else {\n        this.addTradeSignalError = 'Please ensure all fields are correctly formatted.';\n      }\n    },\n\n    // Method to send the \"Add Trade\" signal format\n    sendTradeSignal() {\n      if (this.addTradeSignalError !== '') {\n        console.log('Error: Invalid add trade signal format');\n        return;\n      }\n\n      if (this.addTradeSignal === '') {\n        console.log('Error: No trade signal entered');\n        return;\n      }\n\n      this.isLoading = true;\n\n      const msg = {\n        payload: {\n          addTrade: this.addTradeSignal,\n        },\n      };\n\n      this.send(msg);\n\n      // Reset after sending\n      this.addTradeSignal = '';\n      this.isLoading = false;\n    },\n  },\n  mounted() {\n    this.selfMounted = true;\n  },\n  unmounted() {},\n};\n</script>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":810,"y":80,"wires":[[]]},{"id":"cb79bc4520925e32","type":"ui-base","name":"My UI","path":"/dashboard","appIcon":"home","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"showPageTitle":true,"titleBarStyle":"default"},{"id":"c466b7e4bb472f99","type":"ui-page","name":"Tables","ui":"cb79bc4520925e32","path":"/tables","icon":"table","layout":"tabs","theme":"7ddd4d8503f24d62","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":3,"className":"","visible":"true","disabled":"false"},{"id":"7ddd4d8503f24d62","type":"ui-theme","name":"Cornix Theme","colors":{"surface":"#0f1a3d","primary":"#0f1a3d","bgPage":"#0f1a3d","groupBg":"#0f1a3d","groupOutline":"#0f1a3d"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]

Never mind The problem was in here

this.$socket.emit('ui-event', '8ff25e3069e9bbac', { payload: "master-template-mounted" });

All I did found the EVENT Node ID '8ff25e3069e9bbac' and replace it and now its working

Thank you

Now the problem is now with tabs. I have 2 forms 1st should appear in ADD tab and the 2nd should appear in IMPORT tab. When I deploy because my default tab is ADD the form appear in there, but when I change the tab to IMPORT the 2nd form is not there and when I go back to ADD the 1st form is not there anymore I have to Refresh the page in order to get the 1st form but when I change the tab again starts all over again. Here is the flow

[{"id":"8ff25e3069e9bbac","type":"ui-event","z":"d29a438140be5717","ui":"cb79bc4520925e32","name":"","x":310,"y":340,"wires":[["660824168d458a91","eb406c1bf6250141","f74d9fed9789cfc8"]]},{"id":"890e6be487685a2b","type":"ui-template","z":"d29a438140be5717","group":"","page":"c466b7e4bb472f99","ui":"","name":"ALL TABS ONLY","order":0,"width":0,"height":0,"head":"","format":"<template>\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Pompiere&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n  <v-app>\n    <v-main>\n      <v-card class=\"v-card-tab\">\n        <v-card-title class=\"text-center justify-center py-6\">\n          <!-- Future Space for Content -->\n          <h1 class=\"v-card-title-tap-h1\"></h1>\n        </v-card-title>\n\n        <!-- Tab Selection -->\n        <v-tabs v-model=\"tab\" class=\"v-tabs-style\" grow @change=\"onTabChange\">\n          <v-tab v-for=\"item in items\" :key=\"item\" :text=\"item\" :value=\"item\"></v-tab>\n        </v-tabs>\n\n        <v-tabs-window v-model=\"tab\">\n          <v-tabs-window-item v-for=\"item in items\" :key=\"item\" :value=\"item\">\n            <v-card color=\"basil\" flat>\n              <v-card-text>\n                <!-- Tab Content -->\n                <div v-if=\"tab === 'ADD'\" class=\"d-flex justify-center mb-10 v-sheet-tab-style\">\n                  <v-sheet class=\"ma-8 v-sheet-tab-style\">\n                    <v-card id=\"slot_1\" class=\"mx-auto elevation-20 v-card-tab-style\" style=\"max-width: 800px;\">\n                    </v-card>\n                  </v-sheet>\n                </div>\n\n                <div v-if=\"tab === 'IMPORT'\" class=\"d-flex justify-center mb-10 v-sheet-tab-style\">\n                  <v-sheet class=\"ma-8 v-sheet-tab-style\">\n                    <v-card id=\"slot_2\" class=\"mx-auto elevation-20 v-card-tab-style\" style=\"max-width: 800px;\">\n                    </v-card>\n                  </v-sheet>\n                </div>\n\n                <!-- Repeat similar structure for other tabs -->\n              </v-card-text>\n            </v-card>\n          </v-tabs-window-item>\n        </v-tabs-window>\n      </v-card>\n    </v-main>\n  </v-app>\n</template>\n\n<script>\nexport default {\n  data: () => ({\n    tab: 'ADD', // Default selected tab\n    items: ['ADD', 'IMPORT', 'SEARCH', 'MOVE', 'CHANGE', 'REMOVE', 'ADDS'],\n  }),\n\n  methods: {\n    // Emit an event when a tab is changed\n    onTabChange(newTab) {\n      console.log(`Tab changed to: ${newTab}`);\n      this.$socket.emit('ui-event', '8ff25e3069e9bbac', {\n        payload: `Tab changed to: ${newTab}`,\n      });\n    },\n  },\n\n  mounted() {\n    // Emit an event when the component is mounted\n    this.$socket.emit('ui-event', '8ff25e3069e9bbac', {\n      payload: \"master-template-mounted\",\n    });\n  },\n};\n</script>\n","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":330,"y":180,"wires":[[]]},{"id":"660824168d458a91","type":"ui-template","z":"d29a438140be5717","group":"","page":"c466b7e4bb472f99","ui":"","name":"ADD TRADE TAB","order":0,"width":0,"height":0,"head":"","format":"<template>\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Pompiere&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n\t<Teleport v-if=\"masterMounted && selfMounted\" to=\"#slot_1\">\n\t\t<v-toolbar class=\"v-toolbar-tab-style\" color=\"deep-purple-accent-4\" height=\"120\" cards dark flat>\n\t\t  <div class=\"v-toolbar-tab-div-text-style\">Add New Trade</div>\n\t\t</v-toolbar>\n\t\t<v-form ref=\"formAddTrade\" v-model=\"formAddTrade\" class=\"v-form-tab-style\">\n\t\t  <v-responsive class=\"mx-auto\" style=\"width: 450px;\">\n\t\t\t<v-textarea \n\t\t\t  v-model=\"addTradeSignal\" \n\t\t\t  auto-grow \n\t\t\t  box \n\t\t\t  color=\"deep-purple\" \n\t\t\t  rows=\"6\" \n\t\t\t  clearable \n\t\t\t  @input=\"validateAddTradeSignal\"\n\t\t\t  :error-messages=\"addTradeSignalError\"\n\t\t\t></v-textarea>\n\t\t  </v-responsive>\n\t\t</v-form>\n\t\t<v-divider :thickness=\"2\" class=\"border-opacity-100\" color=\"deep-purple accent-4\"></v-divider>\n\t\t<v-card-actions>\n\t\t  <v-btn \n\t\t\tsize=\"x-large\" \n\t\t\tblock \n\t\t\t:disabled=\"!isValidForm || addTradeSignal === ''\" \n\t\t\t:loading=\"isLoading\" \n\t\t\tclass=\"white--text\" \n\t\t\tcolor=\"deep-purple accent-4\" \n\t\t\t@click=\"sendTradeSignal\" \n\t\t\tdepressed\n\t\t  >\n\t\t\tAdd Trade\n\t\t  </v-btn>\n\t\t</v-card-actions>\n\t</Teleport>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n      addTradeSignal: '', // For the \"Add Trade\" textarea content\n      addTradeSignalError: '', // Error message for invalid input\n      isLoading: false,\n      masterMounted: false,\n      selfMounted: false,\n    };\n  },\n  watch: {\n    msg: function () {\n      if (this.msg?.payload == \"master-template-mounted\") {\n        this.masterMounted = true;\n      }\n    },\n  },\n  computed: {\n    // Computed property to check if the form is valid\n    isValidForm() {\n      return this.addTradeSignalError === '';\n    },\n  },\n  methods: {\n    // Method to validate the \"Add Trade\" signal format\n    validateAddTradeSignal() {\n      const patternTradePair = /^[A-Za-z0-9]+\\/[A-Za-z0-9]+$/;\n      const patternLeverage = /^Leverage Cross \\d+x$/;\n      const patternBuyPrice = /^Buy \\d+(\\.\\d+)?$/;\n      const patternSellPrice = /^Sell \\d+(\\.\\d+)?$/;\n      const patternTradeNumber = /^TRADE #\\d+$/;\n\n      const lines = this.addTradeSignal.split('\\n').map(line => line.trim());\n\n      // If the input does not have exactly 6 lines, invalid\n      if (lines.length !== 6) {\n        this.addTradeSignalError = 'Signal must contain exactly 6 lines.';\n        return;\n      }\n\n      // Check if all the required patterns match the input format\n      const isValid =\n        patternTradePair.test(lines[0]) &&\n        patternLeverage.test(lines[1]) &&\n        patternBuyPrice.test(lines[2]) &&\n        patternSellPrice.test(lines[3]) &&\n        lines[4] === '' &&\n        patternTradeNumber.test(lines[5]);\n\n      // If invalid format, set error message, else clear it\n      if (isValid) {\n        this.addTradeSignalError = ''; // Clear error if valid\n      } else {\n        this.addTradeSignalError = 'Please ensure all fields are correctly formatted.';\n      }\n    },\n\n    // Method to send the \"Add Trade\" signal format\n    sendTradeSignal() {\n      if (this.addTradeSignalError !== '') {\n        console.log('Error: Invalid add trade signal format');\n        return;\n      }\n\n      if (this.addTradeSignal === '') {\n        console.log('Error: No trade signal entered');\n        return;\n      }\n\n      this.isLoading = true;\n\n      const msg = {\n        payload: {\n          addTrade: this.addTradeSignal,\n        },\n      };\n\n      this.send(msg);\n\n      // Reset after sending\n      this.addTradeSignal = '';\n      this.isLoading = false;\n    },\n  },\n  mounted() {\n    this.selfMounted = true;\n  },\n  unmounted() {},\n};\n</script>\n<style>\n\n    .teleported-template {\n        display:none !important;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":810,"y":80,"wires":[["5e89f409df21c9ec"]]},{"id":"f74d9fed9789cfc8","type":"ui-template","z":"d29a438140be5717","group":"","page":"c466b7e4bb472f99","ui":"","name":"IMPORT TRADE TAB","order":0,"width":0,"height":0,"head":"","format":"<template>\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Pompiere&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Annie+Use+Your+Telescope&family=Share:ital,wght@0,400;0,700;1,400;1,700&display=swap\">\n\t<Teleport v-if=\"masterMounted && selfMounted\" to=\"#slot_2\">\n\t\t<v-toolbar class=\"v-toolbar-tab-style\" color=\"deep-purple-accent-4\" height=\"120\" cards dark flat>\n\t\t  <div class=\"v-toolbar-tab-div-text-style\">Import Trade Signal</div>\n\t\t</v-toolbar>\n\t\t<v-form ref=\"formImportTrade\" class=\"v-form-tab-style\">\n\t\t  <v-responsive class=\"mx-auto\" style=\"width: 450px;\">\n\t\t\t<v-textarea\n\t\t\t  v-model=\"importTradeSignal\"\n\t\t\t  auto-grow\n\t\t\t  box\n\t\t\t  color=\"deep-purple\"\n\t\t\t  rows=\"10\"\n\t\t\t  clearable\n\t\t\t  :error-messages=\"importTradeErrorMessage\"\n\t\t\t  @input=\"validateImportTradeSignal\"\n\t\t\t></v-textarea>\n\t\t  </v-responsive>\n\t\t</v-form>\n\t\t<v-divider :thickness=\"2\" class=\"border-opacity-100\" color=\"deep-purple accent-4\"></v-divider>\n\t\t<v-card-actions>\n\t\t  <v-btn\n\t\t\tsize=\"x-large\"\n\t\t\tblock\n\t\t\t:disabled=\"!isFormValid\"\n\t\t\t:loading=\"isLoading\"\n\t\t\tclass=\"white--text\"\n\t\t\tcolor=\"deep-purple accent-4\"\n\t\t\t@click=\"sendImportTrade\"\n\t\t\tdepressed\n\t\t  >\n\t\t\tImport Trade\n\t\t  </v-btn>\n\t\t</v-card-actions>\n\t</Teleport>\n</template>\n\n<script>\nexport default {\n  data() {\n    return {\n\t  importTradeSignal: '', // For the \"Import Trade Signal\" textarea content\n\t  isLoading: false,\n\t  importTradeErrorMessage: '', // Error message for the textarea\n      masterMounted: false,\n      selfMounted: false,\n    };\n  },\n  watch: {\n    importTradeSignal(newVal) {\n      // Automatically validate when the input changes\n      this.validateImportTradeSignal();\n    },\n\t\n    msg: function () {\n      if (this.msg?.payload == \"master-template-mounted\") {\n        this.masterMounted = true;\n      }\n    },\n  },\n  computed: {\n    isFormValid() {\n      // Validate the input only when it's non-empty and valid\n      return this.importTradeSignal.trim() !== '' && this.validateImportTradeSignal();\n    }\n  },\n\n  methods: {\n    // Method to validate \"Import Trade\" signal format\n    validateImportTradeSignal() {\n      const lines = this.importTradeSignal.split('\\n').map(line => line.trim()).filter(line => line !== '');\n\n      if (lines.length % 5 !== 0) {\n        this.importTradeErrorMessage = 'Import trade signal must contain complete trade blocks.';\n        return false;\n      }\n\n      for (let i = 0; i < lines.length; i += 5) {\n        const tradePair = lines[i];\n        const leverage = lines[i + 1];\n        const buyPrice = lines[i + 2];\n        const sellPrice = lines[i + 3];\n        const tradeNumber = lines[i + 4];\n\n        if (!/^[A-Za-z0-9]+\\/[A-Za-z0-9]+$/.test(tradePair)) {\n          this.importTradeErrorMessage = `Invalid trade pair at line ${i + 1}: \"${tradePair}\"`;\n          return false;\n        }\n\n        if (!/^Leverage Cross \\d+x$/.test(leverage)) {\n          this.importTradeErrorMessage = `Invalid leverage format at line ${i + 2}: \"${leverage}\"`;\n          return false;\n        }\n\n        if (!/^Buy \\d+(\\.\\d+)?$/.test(buyPrice)) {\n          this.importTradeErrorMessage = `Invalid buy price at line ${i + 3}: \"${buyPrice}\"`;\n          return false;\n        }\n\n        if (!/^Sell \\d+(\\.\\d+)?$/.test(sellPrice)) {\n          this.importTradeErrorMessage = `Invalid sell price at line ${i + 4}: \"${sellPrice}\"`;\n          return false;\n        }\n\n        if (!/^TRADE #\\d+$/.test(tradeNumber)) {\n          this.importTradeErrorMessage = `Invalid trade number at line ${i + 5}: \"${tradeNumber}\"`;\n          return false;\n        }\n      }\n\n      // Clear any error message if validation passes\n      this.importTradeErrorMessage = '';\n      return true;\n    },\n\n    // Method to send \"Import Trade\" data to Node-RED\n    sendImportTrade() {\n      // Check if the input is valid before sending\n      if (this.importTradeSignal.trim() === '') {\n        this.importTradeErrorMessage = 'Please provide the trade signals.';\n        return; // Prevent sending empty signal\n      }\n\n      if (!this.isFormValid) {\n        return; // Prevent sending if validation fails\n      }\n\n      this.isLoading = true;\n\n      const msg = {\n        payload: {\n          importTrade: this.importTradeSignal,\n        },\n      };\n\n      this.send(msg);\n\n      this.importTradeSignal = ''; // Clear after sending\n      this.isLoading = false; // Reset loading state\n    },\n  },\n\n  mounted() {\n    this.selfMounted = true;\n  },\n  unmounted() {},\n};\n\n</script>\n<style>\n\n    .teleported-template {\n        display:none !important;\n    }\n</style>","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"widget:page","className":"","x":800,"y":200,"wires":[["8413c72a9c8edb72"]]},{"id":"cb79bc4520925e32","type":"ui-base","name":"My UI","path":"/dashboard","appIcon":"home","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control"],"showPathInSidebar":false,"showPageTitle":true,"titleBarStyle":"default"},{"id":"c466b7e4bb472f99","type":"ui-page","name":"Tables","ui":"cb79bc4520925e32","path":"/tables","icon":"table","layout":"tabs","theme":"7ddd4d8503f24d62","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":3,"className":"","visible":"true","disabled":"false"},{"id":"7ddd4d8503f24d62","type":"ui-theme","name":"Cornix Theme","colors":{"surface":"#0f1a3d","primary":"#0f1a3d","bgPage":"#0f1a3d","groupBg":"#0f1a3d","groupOutline":"#0f1a3d"},"sizes":{"density":"default","pagePadding":"12px","groupGap":"12px","groupBorderRadius":"4px","widgetGap":"12px"}}]

It was hard to understand the first explanation, now it's worse a lot. You a described outcome but nearly nothing about the real target of your build.

But I'm away from computer anyway. Can't help before Monday.

I have two forms set up in my Node-RED dashboard. The first form should appear in the "ADD" tab, and the second form should appear in the "IMPORT" tab. However, I'm facing the following issues:

When I deploy the flow, the first form shows up correctly in the "ADD" tab (my default tab).
If I switch to the "IMPORT" tab, the second form does not appear.
When I switch back to the "ADD" tab, the first form also disappears.
To make either form appear again, I have to refresh the page.

This cycle repeats whenever I change tabs. How can I fix this behavior so that each form consistently appears in its respective tab?

Is that more understandable!!

Sounds like this bug

Is the bug resolved yet?

Seems that not yet.

Any other method of doing that?

Hi do you know any other method of doing that?

The work around is to refresh the browser page after a deploy.