How to change page with ui_builder depending on the decision?

Hi, so I've been trying to work on setting up a log in page using the ui_builder node. I've managed to get the form and such ready but I'm stuck at making the decision since I havea few nodes connected to it. Should I be doing something like naming that msg.payload with topic and then have the index.js process the information and do a href?

Thanks again in advance

[{"id":"ca8858e5.e11148","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"a516485d.f47a38","type":"ui_form","z":"ca8858e5.e11148","name":"","label":"User Login","group":"110100c2.7c16b7","order":3,"width":12,"height":1,"options":[{"label":"User Name","value":"LogInUserName","type":"text","required":true,"rows":null},{"label":"Password","value":"LogInPassword","type":"password","required":true,"rows":null}],"formValue":{"LogInUserName":"","LogInPassword":""},"payload":"","submit":"submit","cancel":"","topic":"topic","topicType":"msg","splitLayout":"","x":250,"y":180,"wires":[["a12e9909.c9b8e8","c8468fca.9b0b98"]]},{"id":"a12e9909.c9b8e8","type":"MSSQL","z":"ca8858e5.e11148","mssqlCN":"68b3ed78.004e8c","name":"MSSQL","outField":"payload","returnType":0,"throwErrors":1,"query":"SELECT * FROM [NodeRedLogIn].[dbo].[NodeRedLogInData] WHERE [NodeRedLogIn].[dbo].[NodeRedLogInData].[UserName] = '{{{payload.LogInUserName}}}' AND [NodeRedLogIn].[dbo].[NodeRedLogInData].[UserPassword] = '{{{payload.LogInPassword}}}'","modeOpt":"","modeOptType":"query","queryOpt":"","queryOptType":"editor","paramsOpt":"","paramsOptType":"none","rows":"rows","rowsType":"msg","params":[],"x":400,"y":180,"wires":[["11583af2.fba415","eeaedd08.273168"]]},{"id":"11583af2.fba415","type":"function","z":"ca8858e5.e11148","name":"Check Login","func":"// Check if login successful\nif(msg.payload.length >0)\n{\n    flow.set(\"CurrentUser\", msg.payload[0].UserName); \n    flow.set(\"UserType\", msg.payload[0].UserType);\n    msg.payload = 1;\n}\nelse\n{\n    msg.payload = 0;\n}\n\nmsg.topic = \"decision\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":180,"wires":[["64f1af0e.04b6d8","6fb9632a.96065c","9c1dd8c5.6a04d"]]},{"id":"eeaedd08.273168","type":"debug","z":"ca8858e5.e11148","name":"Result","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":220,"wires":[]},{"id":"64f1af0e.04b6d8","type":"debug","z":"ca8858e5.e11148","name":"Result","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":690,"y":220,"wires":[]},{"id":"1d2ed4d7.eb5b7b","type":"comment","z":"ca8858e5.e11148","name":"Log In Page","info":"","x":250,"y":100,"wires":[]},{"id":"6fb9632a.96065c","type":"ui_ui_control","z":"ca8858e5.e11148","name":"Next Page","events":"change","x":710,"y":180,"wires":[[]],"inputLabels":["1"]},{"id":"c8468fca.9b0b98","type":"debug","z":"ca8858e5.e11148","name":"Result","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":390,"y":220,"wires":[]},{"id":"46fe5375.081964","type":"comment","z":"ca8858e5.e11148","name":"Homepage","info":"","x":240,"y":300,"wires":[]},{"id":"a2ee8183.549c2","type":"ui_text","z":"ca8858e5.e11148","group":"77b07c2f.8bff2c","order":3,"width":6,"height":1,"name":"","label":"Log In Successful","format":"{{msg.payload}}","layout":"row-center","x":270,"y":340,"wires":[]},{"id":"a3c87530.d8c5b8","type":"ui_button","z":"ca8858e5.e11148","name":"","group":"77b07c2f.8bff2c","order":5,"width":4,"height":1,"passthru":false,"label":"Log Out","tooltip":"","color":"","bgcolor":"","icon":"","payload":"0","payloadType":"num","topic":"topic","topicType":"msg","x":240,"y":380,"wires":[["3ec79daf.0de81a","5d45a62.a66ab58"]]},{"id":"3ec79daf.0de81a","type":"function","z":"ca8858e5.e11148","name":"","func":"flow.set(\"CurrentUser\", \"None\");\nflow.set(\"UserType\", \"None\");\nmsg.payload = \"User Logged Out!\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":420,"wires":[["a52e8c6d.ffeaf","43142c46.a253dc","ceacd0c.6b2f33"]]},{"id":"5d45a62.a66ab58","type":"ui_ui_control","z":"ca8858e5.e11148","name":"Log Out","events":"change","x":400,"y":380,"wires":[[]]},{"id":"9c1dd8c5.6a04d","type":"function","z":"ca8858e5.e11148","name":"Indicate Wrong Info","func":"if(msg.payload == 0)\n{\n    msg.payload = \"Please enter the correct User Name and Password!\";\n    msg.color = \"red\";\n}\nelse\n{\n    msg.payload = \"User Logged In!\"\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":730,"y":260,"wires":[["a52e8c6d.ffeaf","43142c46.a253dc","13a0b37a.21f105"]]},{"id":"a52e8c6d.ffeaf","type":"ui_text","z":"ca8858e5.e11148","group":"110100c2.7c16b7","order":8,"width":0,"height":0,"name":"","label":"","format":"<font color= {{msg.color}} > {{msg.payload}} </font>","layout":"col-center","x":890,"y":260,"wires":[]},{"id":"98f13f35.95f9a","type":"ui_text","z":"ca8858e5.e11148","group":"11e5bd23.ba4c03","order":2,"width":24,"height":1,"name":"","label":"Log In Menu","format":"{{msg.payload}}","layout":"row-center","x":250,"y":140,"wires":[]},{"id":"43142c46.a253dc","type":"uibuilder","z":"ca8858e5.e11148","name":"","topic":"","url":"login","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":432000,"tokenAutoExtend":false,"reload":false,"x":230,"y":220,"wires":[["a12e9909.c9b8e8","c8468fca.9b0b98"],[]]},{"id":"13a0b37a.21f105","type":"debug","z":"ca8858e5.e11148","name":"Result","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":890,"y":220,"wires":[]},{"id":"ceacd0c.6b2f33","type":"debug","z":"ca8858e5.e11148","name":"Result","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":530,"y":420,"wires":[]},{"id":"38abbf52.833f6","type":"uibuilder","z":"ca8858e5.e11148","name":"","topic":"","url":"homepage","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"useSecurity":false,"sessionLength":432000,"tokenAutoExtend":false,"reload":false,"x":250,"y":420,"wires":[["5d45a62.a66ab58","3ec79daf.0de81a"],[]]},{"id":"66b91a82.45bafc","type":"comment","z":"ca8858e5.e11148","name":"index.css","info":"/* Cloak elements on initial load to hide the possible display of {{ ... }} \n * Add to the app tag or to specific tags\n * To display \"loading...\", change to the following:\n *    [v-cloak] > * { display:none }\n *    [v-cloak]::before { content: \"loading…\" }\n */\n[v-cloak] { display: none; }\n\n/*  Colours for Syntax Highlighted pre's */\n.syntax-highlight {color:white;background-color:black;padding:5px 10px;}\n.syntax-highlight > .key {color:#ffbf35}\n.syntax-highlight > .string {color:#5dff39;}\n.syntax-highlight > .number {color:#70aeff;}\n.syntax-highlight > .boolean {color:#b993ff;}","x":400,"y":100,"wires":[]},{"id":"3c52c748.667a5","type":"comment","z":"ca8858e5.e11148","name":"index.html","info":"<!doctype html>\n<html lang=\"en\">\n<head>\n\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <title>Node-RED UI Builder - VueJS + bootstrap-vue default template</title>\n    <meta name=\"description\" content=\"Node-RED UI Builder - VueJS + bootstrap-vue default template\">\n\n    <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css\" />\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css\" />\n    <!-- Your own CSS -->\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n</head>\n\n<body>\n    \n    <!-- Start of Making the Form -->\n    <div id = \"app\" v-cloak>\n        <b-container id = \"app container\">\n            <b-img src=\"./images/node-blue-192x192.png\" rounded left v-bind=\"imgProps\" alt=\"Blue Node-RED\" class=\"mt-1 mr-2\"></b-img>\n            <h1> Node-RED Homepage </h1>\n            <b-card style=\"width: 245px; margin-left:auto; margin-right: auto;\">\n                <h3 slot = \"header\"> User Log In </h3>\n                <form action=\"\" method=\"get\" accept-charset=\"utf-8\">\n                    <label for=\"LogInUserName\"> User Name: </label><br>\n                    <b-form-input v-model=\"LogInUserName\" type=\"text\" placeholder=\"User Name\"></b-form-input><br>\n                    <label for=\"LogInPassword\" style=\"margin-left: auto;l margin-right: auto;\"> Password: </label><br>\n                    <b-form-input v-model=\"LogInPassword\" type=\"password\" placeholder=\"Password\"></b-form-input><br>\n                    <div><span id=\"received\"></span></div>\n                    <b-button id=\"myButton1\" pill variant=\"primary\" v-on:click=\"login\" href=\"http://127.0.0.1:1880/homepage/\">Login</b-button>\n                </form>\n            </b-card>\n        </b-container>\n    </div>\n    <!-- End of Making the Form -->\n    \n    <!-- These MUST be in the right order. Note no leading / -->\n\n    <!-- REQUIRED: Socket.IO is loaded only once for all instances. Without this, you don't get a websocket connection -->\n    <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n\n    <!-- Vendor Libraries - Load in the right order, use minified, production versions for speed -->\n    <script src=\"../uibuilder/vendor/vue/dist/vue.js\"></script> <!-- dev version with component compiler -->\n    <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script>   prod version with component compiler -->\n    <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.runtime.min.js\"></script>   prod version without component compiler -->\n    <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js\"></script> <!-- Dev version -->\n    <!-- <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js\"></script>   Prod version -->\n\n    <!-- REQUIRED: Sets up Socket listeners and the msg object -->\n    <script src=\"./uibuilderfe.js\"></script> <!-- dev version -->\n    <!-- <script src=\"./uibuilderfe.min.js\"></script>     prod version -->\n\n    <!-- OPTIONAL: You probably want this. Put your custom code here -->\n    <script src=\"./index.js\"></script>\n    \n</body>\n\n</html>","x":540,"y":100,"wires":[]},{"id":"fc63fde6.aab978","type":"comment","z":"ca8858e5.e11148","name":"index.js","info":"/* jshint browser: true, esversion: 5, asi: true */\n/*globals Vue, uibuilder */\n// @ts-nocheck\n/*\n  Copyright (c) 2021 Julian Knight (Totally Information)\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n*/\n'use strict'\n\n/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */\n\n// eslint-disable-next-line no-unused-vars\nconst app = new Vue({\n    el: '#app',\n\n    data() { return {\n\n        startMsg    : 'Vue has started, waiting for messages',\n        feVersion   : '',\n        counterBtn  : 0,\n        inputText   : null,\n        inputChkBox : false,\n        socketConnectedState : false,\n        serverTimeOffset     : '[unknown]',\n        imgProps             : { width: 75, height: 75 },\n\n        msgRecvd    : '[Nothing]',\n        msgsReceived: 0,\n        msgCtrl     : '[Nothing]',\n        msgsControl : 0,\n\n        msgSent     : '[Nothing]',\n        msgsSent    : 0,\n        msgCtrlSent : '[Nothing]',\n        msgsCtrlSent: 0,\n\n        isLoggedOn  : false,\n        userId      : null,\n        userPw      : null,\n        inputId     : '',\n\n    }}, // --- End of data --- //\n\n    methods: {\n\n        // Called from the login button - sends a msg to Node-RED\n        login: function(event) {\n            var topic = this.msgRecvd.topic || 'uibuilder/vue'\n            uibuilder.send( {\n                'topic': topic,\n                'payload': {\n                    'LogInUserName': this.LogInUserName,\n                    'LogInPassword': this.LogInPassword\n                }\n            } )\n\n        }, // --- End of login --- //\n\n        // return formatted HTML version of JSON object\n        syntaxHighlight: function(json) {\n            json = JSON.stringify(json, undefined, 4)\n            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n            json = json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\n                var cls = 'number'\n                if (/^\"/.test(match)) {\n                    if (/:$/.test(match)) {\n                        cls = 'key'\n                    } else {\n                        cls = 'string'\n                    }\n                } else if (/true|false/.test(match)) {\n                    cls = 'boolean'\n                } else if (/null/.test(match)) {\n                    cls = 'null'\n                }\n                return '<span class=\"' + cls + '\">' + match + '</span>'\n            })\n            return json\n        }, // --- End of syntaxHighlight --- //\n\n    }, // --- End of methods --- //\n\n    // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured\n\n    /** Called after the Vue app has been created. A good place to put startup code */\n    created: function() {\n\n        // Example of retrieving data from uibuilder\n        this.feVersion = uibuilder.get('version')\n\n        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3\n         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder\n         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.\n         * e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names\n         * @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.\n         * @param {string=} ioPath Optional. changes self.ioPath from the default\n         * @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.\n         */\n        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.\n\n        //console.log(this)\n\n    }, // --- End of created hook --- //\n\n    /** Called once all Vue component instances have been loaded and the virtual DOM built */\n    mounted: function(){\n\n        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')\n\n        var app = this  // Reference to `this` in case we need it for more complex functions\n\n        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO\n        uibuilder.onChange('msg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)\n            app.msgRecvd = msg\n            app.msgsReceived = uibuilder.get('msgsReceived')\n        })\n\n        //#region ---- Debug info, can be removed for live use ---- //\n\n        /** You can use the following to help trace how messages flow back and forth.\n         * You can then amend this processing to suite your requirements.\n         */\n\n        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable\n        uibuilder.onChange('ctrlMsg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)\n            app.msgCtrl = msg\n            app.msgsControl = uibuilder.get('msgsCtrl')\n        })\n\n        /** You probably only need these to help you understand the order of processing\n         * If a message is sent back to Node-RED, we can grab a copy here if we want to\n         */\n        uibuilder.onChange('sentMsg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)\n            app.msgSent = msg\n            app.msgsSent = uibuilder.get('msgsSent')\n        })\n\n        /** If we send a control message to Node-RED, we can get a copy of it here */\n        uibuilder.onChange('sentCtrlMsg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)\n            app.msgCtrlSent = msg\n            app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')\n        })\n\n        /** If Socket.IO connects/disconnects, we get true/false here */\n        uibuilder.onChange('ioConnected', function(connected){\n            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)\n            app.socketConnectedState = connected\n        })\n        /** If Server Time Offset changes */\n        uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){\n            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)\n            app.serverTimeOffset = serverTimeOffset\n        })\n\n        /** If user is logged on/off */\n        uibuilder.onChange('isAuthorised', function(isAuthorised){\n            //console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)\n            //console.log('authData: ', uibuilder.get('authData'))\n            //console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))\n            app.isLoggedOn = isAuthorised\n        })\n        \n        uibuilder.onChange('msg', function (msg) {\n\t        console.log(msg);\n        \tdocument.getElementById('received').innerHTML = msg.payload;\n        })\n\n        //#endregion ---- Debug info, can be removed for live use ---- //\n\n    }, // --- End of mounted hook --- //\n\n}) // --- End of app1 --- //\n\n// EOF","x":670,"y":100,"wires":[]},{"id":"6b065072.eae028","type":"comment","z":"ca8858e5.e11148","name":"index.css","info":"/* Cloak elements on initial load to hide the possible display of {{ ... }} \n * Add to the app tag or to specific tags\n * To display \"loading...\", change to the following:\n *    [v-cloak] > * { display:none }\n *    [v-cloak]::before { content: \"loading…\" }\n */\n[v-cloak] { display: none; }\n\n/*  Colours for Syntax Highlighted pre's */\n.syntax-highlight {color:white;background-color:black;padding:5px 10px;}\n.syntax-highlight > .key {color:#ffbf35}\n.syntax-highlight > .string {color:#5dff39;}\n.syntax-highlight > .number {color:#70aeff;}\n.syntax-highlight > .boolean {color:#b993ff;}","x":400,"y":300,"wires":[]},{"id":"9c75d63a.9200d8","type":"comment","z":"ca8858e5.e11148","name":"index.html","info":"<!doctype html>\n<html lang=\"en\">\n<head>\n\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n    <title>Node-RED UI Builder - VueJS + bootstrap-vue default template</title>\n    <meta name=\"description\" content=\"Node-RED UI Builder - VueJS + bootstrap-vue default template\">\n\n    <link rel=\"icon\" href=\"./images/node-blue.ico\">\n\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css\" />\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css\" />\n    <!-- Your own CSS -->\n    <link type=\"text/css\" rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n</head>\n\n<body>\n    \n    <!-- Start of Making the Form -->\n    <div id = \"app\" v-cloak>\n        <b-container id = \"app container\">\n            <b-img src=\"./images/node-blue-192x192.png\" rounded left v-bind=\"imgProps\" alt=\"Blue Node-RED\" class=\"mt-1 mr-2\"></b-img>\n            <h1> Node-RED Homepage </h1>\n            <b-card style=\"width: 245px; margin-left:auto; margin-right: auto;\">\n                <h3 slot = \"header\"> Homepage </h3>\n                <form action=\"\" method=\"get\" accept-charset=\"utf-8\">\n                    <b-button id=\"myButton2\" pill variant=\"primary\" v-on:click=\"logout\" href=\"http://127.0.0.1:1880/login/\">Logout</b-button>\n                </form>\n            </b-card>\n        </b-container>\n    </div>\n    <!-- End of Making the Form -->\n    \n    <!-- These MUST be in the right order. Note no leading / -->\n\n    <!-- REQUIRED: Socket.IO is loaded only once for all instances. Without this, you don't get a websocket connection -->\n    <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n\n    <!-- Vendor Libraries - Load in the right order, use minified, production versions for speed -->\n    <script src=\"../uibuilder/vendor/vue/dist/vue.js\"></script> <!-- dev version with component compiler -->\n    <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script>   prod version with component compiler -->\n    <!-- <script src=\"../uibuilder/vendor/vue/dist/vue.runtime.min.js\"></script>   prod version without component compiler -->\n    <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js\"></script> <!-- Dev version -->\n    <!-- <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js\"></script>   Prod version -->\n\n    <!-- REQUIRED: Sets up Socket listeners and the msg object -->\n    <script src=\"./uibuilderfe.js\"></script> <!-- dev version -->\n    <!-- <script src=\"./uibuilderfe.min.js\"></script>     prod version -->\n\n    <!-- OPTIONAL: You probably want this. Put your custom code here -->\n    <script src=\"./index.js\"></script>\n    \n</body>\n\n</html>","x":540,"y":300,"wires":[]},{"id":"6ca64736.d8dcc8","type":"comment","z":"ca8858e5.e11148","name":"index.js","info":"/* jshint browser: true, esversion: 5, asi: true */\n/*globals Vue, uibuilder */\n// @ts-nocheck\n/*\n  Copyright (c) 2021 Julian Knight (Totally Information)\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n*/\n'use strict'\n\n/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */\n\n// eslint-disable-next-line no-unused-vars\nconst app = new Vue({\n    el: '#app',\n\n    data() { return {\n\n        startMsg    : 'Vue has started, waiting for messages',\n        feVersion   : '',\n        counterBtn  : 0,\n        inputText   : null,\n        inputChkBox : false,\n        socketConnectedState : false,\n        serverTimeOffset     : '[unknown]',\n        imgProps             : { width: 75, height: 75 },\n\n        msgRecvd    : '[Nothing]',\n        msgsReceived: 0,\n        msgCtrl     : '[Nothing]',\n        msgsControl : 0,\n\n        msgSent     : '[Nothing]',\n        msgsSent    : 0,\n        msgCtrlSent : '[Nothing]',\n        msgsCtrlSent: 0,\n\n        isLoggedOn  : false,\n        userId      : null,\n        userPw      : null,\n        inputId     : '',\n\n    }}, // --- End of data --- //\n\n    computed: {\n\n        hLastRcvd: function() {\n            var msgRecvd = this.msgRecvd\n            if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd\n            else return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)\n        },\n        hLastSent: function() {\n            var msgSent = this.msgSent\n            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent\n            else return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)\n        },\n        hLastCtrlRcvd: function() {\n            var msgCtrl = this.msgCtrl\n            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl\n            else return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)\n        },\n        hLastCtrlSent: function() {\n            var msgCtrlSent = this.msgCtrlSent\n            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent\n            //else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrlSent])\n            else return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)\n        },\n\n    }, // --- End of computed --- //\n\n    methods: {\n\n        \n\n        // REALLY Simple method to return DOM events back to Node-RED. See the 2nd b-button on the default html\n        doEvent: uibuilder.eventSend,\n\n        // return formatted HTML version of JSON object\n        syntaxHighlight: function(json) {\n            json = JSON.stringify(json, undefined, 4)\n            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\n            json = json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\n                var cls = 'number'\n                if (/^\"/.test(match)) {\n                    if (/:$/.test(match)) {\n                        cls = 'key'\n                    } else {\n                        cls = 'string'\n                    }\n                } else if (/true|false/.test(match)) {\n                    cls = 'boolean'\n                } else if (/null/.test(match)) {\n                    cls = 'null'\n                }\n                return '<span class=\"' + cls + '\">' + match + '</span>'\n            })\n            return json\n        }, // --- End of syntaxHighlight --- //\n\n    }, // --- End of methods --- //\n\n    // Available hooks: beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed, activated,deactivated, errorCaptured\n\n    /** Called after the Vue app has been created. A good place to put startup code */\n    created: function() {\n\n        // Example of retrieving data from uibuilder\n        this.feVersion = uibuilder.get('version')\n\n        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3\n         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder\n         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.\n         * e.g. uibuilder.start('/uib', '/uibuilder/vendor/socket.io') // change to use your paths/names\n         * @param {Object=|string=} namespace Optional. Object containing ref to vueApp, Object containing settings, or String IO Namespace override. changes self.ioNamespace from the default.\n         * @param {string=} ioPath Optional. changes self.ioPath from the default\n         * @param {Object=} vueApp Optional. Reference to the VueJS instance. Used for Vue extensions.\n         */\n        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.\n\n        //console.log(this)\n\n    }, // --- End of created hook --- //\n\n    /** Called once all Vue component instances have been loaded and the virtual DOM built */\n    mounted: function(){\n\n        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')\n\n        var app = this  // Reference to `this` in case we need it for more complex functions\n\n        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO\n        uibuilder.onChange('msg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)\n            app.msgRecvd = msg\n            app.msgsReceived = uibuilder.get('msgsReceived')\n        })\n\n        //#region ---- Debug info, can be removed for live use ---- //\n\n        /** You can use the following to help trace how messages flow back and forth.\n         * You can then amend this processing to suite your requirements.\n         */\n\n        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable\n        uibuilder.onChange('ctrlMsg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', msg)\n            app.msgCtrl = msg\n            app.msgsControl = uibuilder.get('msgsCtrl')\n        })\n\n        /** You probably only need these to help you understand the order of processing\n         * If a message is sent back to Node-RED, we can grab a copy here if we want to\n         */\n        uibuilder.onChange('sentMsg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', msg)\n            app.msgSent = msg\n            app.msgsSent = uibuilder.get('msgsSent')\n        })\n\n        /** If we send a control message to Node-RED, we can get a copy of it here */\n        uibuilder.onChange('sentCtrlMsg', function(msg){\n            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', msg)\n            app.msgCtrlSent = msg\n            app.msgsCtrlSent = uibuilder.get('msgsSentCtrl')\n        })\n\n        /** If Socket.IO connects/disconnects, we get true/false here */\n        uibuilder.onChange('ioConnected', function(connected){\n            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', connected)\n            app.socketConnectedState = connected\n        })\n        /** If Server Time Offset changes */\n        uibuilder.onChange('serverTimeOffset', function(serverTimeOffset){\n            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', serverTimeOffset)\n            app.serverTimeOffset = serverTimeOffset\n        })\n\n        /** If user is logged on/off */\n        uibuilder.onChange('isAuthorised', function(isAuthorised){\n            //console.info('[indexjs:uibuilder.onChange:isAuthorised] isAuthorised changed. User logged on?:', isAuthorised)\n            //console.log('authData: ', uibuilder.get('authData'))\n            //console.log('authTokenExpiry: ', uibuilder.get('authTokenExpiry'))\n            app.isLoggedOn = isAuthorised\n        })\n        \n        document.getElementById('myButton2').onclick = function () {\n\t        uibuilder.send({ payload: 0 });\n};\n\n        //#endregion ---- Debug info, can be removed for live use ---- //\n\n    }, // --- End of mounted hook --- //\n\n}) // --- End of app1 --- //\n\n// EOF","x":670,"y":300,"wires":[]},{"id":"110100c2.7c16b7","type":"ui_group","name":"LogIn","tab":"3a9a4788.4f2808","order":2,"disp":false,"width":30,"collapse":false},{"id":"68b3ed78.004e8c","type":"MSSQL-CN","tdsVersion":"7_4","name":"NodeRedLogIn","server":"ITC-NB-23","port":"1433","encyption":false,"trustServerCertificate":false,"database":"NodeRedLogIn","useUTC":false,"connectTimeout":"15000","requestTimeout":"15000","cancelTimeout":"5000","pool":"5","parseJSON":false,"enableArithAbort":false},{"id":"77b07c2f.8bff2c","type":"ui_group","name":"HomePage","tab":"8aecd779.d9e6e","order":1,"disp":false,"width":"30","collapse":false},{"id":"11e5bd23.ba4c03","type":"ui_group","name":"Logo","tab":"3a9a4788.4f2808","order":1,"disp":false,"width":30,"collapse":false},{"id":"3a9a4788.4f2808","type":"ui_tab","name":"UserLogIn","icon":"dashboard","order":1,"disabled":false,"hidden":false},{"id":"8aecd779.d9e6e","type":"ui_tab","name":"Homepage","icon":"dashboard","order":2,"disabled":false,"hidden":false}]

Hi, the easiest way is to keep everything in a single-page app with components that you show/hide depending on requirements.

In that case, yes, using the msg.topic is a good way to differentiate your payloads.

For example, I have a page that shows a login form if the client is not authenticated and the logout button if the client is authenticated.

That way, you don't have to loose the context of the page your user is on and they can retain all the data. If you decide to use a multi-page approach, you will likely need to track sessions in Node-RED - it is more complex but still can be done.

1 Like

Ah ok, so I would have to do similar stuff like the ui_control? I have yet looked into that yet but would the coding method be similar to the ui_control?

I've not used ui_control.

Here is an example from my own home automation:

Multiple inputs go to the single uibuilder node which is an SPA with multiple tabs.

The index.js in the front-end SPA sorts out the input messages from Node-RED and updates the UI components accordingly.

As you can see, I need to replace some batteries around the house :slight_smile:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.