Uibuilder vue-router without compiling

Hello!
First of all, I would like to thank the great work done by all the people in this forum!
This is my first time with uibuilder and I've been testing it with some of its features. Now I want to go a little further and try vue-router without a build step but I can't get it to work.
I have tried to make a very simple test with the information that appears in (https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Using-Vue-Router) but I can't make it works. I want to do a simple two "pages" navigation: "home" and "about".
This is my folder structure:
imagen
With index.js:

'use strict'


import router from './router.js';

const app1 = new Vue({
    el: '#app',
    data: {
        startMsg: 'Vue has started, waiting for messages',
        feVersion: '',
        counterBtn: 0,
        inputText: null,
        inputChkBox: false,
        socketConnectedState: false,
        serverTimeOffset: '[unknown]',
        imgProps: { width: 75, height: 75 },

        msgRecvd: '[Nothing]',
        msgsReceived: 0,
        msgCtrl: '[Nothing]',
        msgsControl: 0,

        msgSent: '[Nothing]',
        msgsSent: 0,
        msgCtrlSent: '[Nothing]',
        msgsCtrlSent: 0,
    }, // --- End of data --- //
    computed: {
        hLastRcvd: function () {
            var msgRecvd = this.msgRecvd
            if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
            else return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
        },
        hLastSent: function () {
            var msgSent = this.msgSent
            if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
            else return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
        },
        hLastCtrlRcvd: function () {
            var msgCtrl = this.msgCtrl
            if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
            else return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
        },
        hLastCtrlSent: function () {
            var msgCtrlSent = this.msgCtrlSent
            if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
            //else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrlSent])
            else return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
        },
    }, // --- End of computed --- //
    methods: {
        increment: function () {
            // Increment the count by one
            this.counterBtn = this.counterBtn + 1
            var topic = this.msgRecvd.topic || 'uibuilder/vue'
            uibuilder.send({
                'topic': topic,
                'payload': {
                    'type': 'counterBtn',
                    'btnCount': this.counterBtn,
                    'message': this.inputText,
                    'inputChkBox': this.inputChkBox
                }
            })
        }, // --- End of increment --- //

        // return formatted HTML version of JSON object
        syntaxHighlight: function (json) {
            json = JSON.stringify(json, undefined, 4)
            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
                var cls = 'number'
                if (/^"/.test(match)) {
                    if (/:$/.test(match)) {
                        cls = 'key'
                    } else {
                        cls = 'string'
                    }
                } else if (/true|false/.test(match)) {
                    cls = 'boolean'
                } else if (/null/.test(match)) {
                    cls = 'null'
                }
                return '<span class="' + cls + '">' + match + '</span>'
            })
            return json
        }, // --- End of syntaxHighlight --- //
    }, // --- End of methods --- //

    // Available hooks: init,mounted,updated,destroyed
    mounted: function () {
        //console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')

        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/nr/uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names
         */
        uibuilder.start()

        var vueApp = this

        // Example of retrieving data from uibuilder
        vueApp.feVersion = uibuilder.get('version')

        /** You can use the following to help trace how messages flow back and forth.
         * You can then amend this processing to suite your requirements.
         */

        //#region ---- Trace Received Messages ---- //
        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        // newVal relates to the attribute being listened to.
        uibuilder.onChange('msg', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', newVal)
            vueApp.msgRecvd = newVal
        })
        // As we receive new messages, we get an updated count as well
        uibuilder.onChange('msgsReceived', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange] Updated count of received msgs:', newVal)
            vueApp.msgsReceived = newVal
        })

        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
        uibuilder.onChange('ctrlMsg', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', newVal)
            vueApp.msgCtrl = newVal
        })
        // Updated count of control messages received
        uibuilder.onChange('msgsCtrl', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:msgsCtrl] Updated count of received CONTROL msgs:', newVal)
            vueApp.msgsControl = newVal
        })
        //#endregion ---- End of Trace Received Messages ---- //

        //#region ---- Trace Sent Messages ---- //
        // You probably only need these to help you understand the order of processing //
        // If a message is sent back to Node-RED, we can grab a copy here if we want to
        uibuilder.onChange('sentMsg', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', newVal)
            vueApp.msgSent = newVal
        })
        // Updated count of sent messages
        uibuilder.onChange('msgsSent', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:msgsSent] Updated count of msgs sent:', newVal)
            vueApp.msgsSent = newVal
        })

        // If we send a control message to Node-RED, we can get a copy of it here
        uibuilder.onChange('sentCtrlMsg', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', newVal)
            vueApp.msgCtrlSent = newVal
        })
        // And we can get an updated count
        uibuilder.onChange('msgsSentCtrl', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:msgsSentCtrl] Updated count of CONTROL msgs sent:', newVal)
            vueApp.msgsCtrlSent = newVal
        })
        //#endregion ---- End of Trace Sent Messages ---- //

        // If Socket.IO connects/disconnects, we get true/false here
        uibuilder.onChange('ioConnected', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', newVal)
            vueApp.socketConnectedState = newVal
        })
        // If Server Time Offset changes
        uibuilder.onChange('serverTimeOffset', function (newVal) {
            //console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', newVal)
            vueApp.serverTimeOffset = newVal
        })

    }, // --- End of mounted hook --- //
  
    router: new VueRouter(router),

}) // --- End of app1 --- //

// EOF

index.html:

<!doctype html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">

    <title>Node-RED UI Builder - VueJS + bootstrap-vue version</title>
    <meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue version">

    <link rel="icon" href="./images/node-blue.ico">

    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link rel="stylesheet" href="./index.css" media="all">
</head>

<body>
    <div id="app">
        <p>Loaded</p>
        <div>
            <b-nav>
              <b-nav-item active>Active</b-nav-item>
              <b-nav-item to="/home">Home</b-nav-item>
              <b-nav-item to="/about">About</b-nav-item>
              
            </b-nav>
          </div>    
          <b-container fluid>
            <router-view></router-view>
        </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>

    <!-- HTTP Vue Loader -->
    <script src="https://unpkg.com/http-vue-loader"></script>

    <!-- VueX -->
    <script src="../uibuilder/vendor/vuex/dist/vuex.js"></script>
    <!-- Vue Router -->
    <script src="../uibuilder/vendor/vue-router/dist/vue-router.js"></script>


    <script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
    <script src="./index.js" type="module"></script>

</body>

</html>

router.js:

const Home = httpVueLoader('./views/home.vue')
const About= httpVueLoader('./views/about.vue')


export default {
    routes: [
        {
            path: '/',
            name: 'home',
            components: Home,
            
        },
            
        {
            path: '/',
            name: 'about',
            components: About,
            
        },
    ],
};

and .vue files are very simple:

<template>
    <b-row>
    this is about page
    </b-row>
</template>

Please, Can someone help me or share some simple example?
Thanks!

Hi, I'm afraid I've still not got round to using Vue Router. However, long-standing forum member and friend of uibuilder @afelix provided a write-up of using it some while back. It is on the WIKI:

Do remember that this was written for Vue2 not Vue3.

Hi! Thanks for the quick answer!
... yes, this is the write-up I've tried to use as base example, but it doesn't work for me... It seems be part of his dashboard and it got elements I doesn't understand without more iformation, like store.js and paths.
Do you know another way to do a dashboard with route navigation? With this example ([Preformatted text](https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Dynamically-load-.vue-files-without-a-build-step)) I can show .vue content but I can't do real navigation because I can't point to from nav or nav-bar.
Regards!

Are you getting any errors in the browser console?

Also, can you work up a simple example index.js and index.html that shows the issue?

Hi, Sorry, I didn't explain myself properly. This example ([Preformatted text](https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Dynamically-load-.vue-files-without-a-build-step)) works ok, but I don't know any mode to do "real" navigation because I don't know any way to link nav or navbar

<div>
  <b-nav>
    <b-nav-item active>Active</b-nav-item>
    <b-nav-item>Link</b-nav-item>
    <b-nav-item>Another Link</b-nav-item>
    <b-nav-item disabled>Disabled</b-nav-item>
  </b-nav>
</div>

to my-component dinamically

 <my-component :greeting="myGreeting" :who="myWho">
                <p>This text goes into the &lt;slot&gt; tags in the component. try removing this from index.html to see the default text.</p>
            </my-component>

Do yo know anyway to do that?
Regards!

Oh, I see.

So that is standard Vue. Put the data for all of your links into a managed vue data variable which you can then update from incoming msgs.

Then change the html and instead of manually writing the b-nav-item entries, you write a single one with variables and wrap that in a Vue v-for.

Thanks! Good Idea!
I'll try do that. I'm going to look for more information about "managed vue data variables".
Thanks again!

A managed variable is simply one that you put in the data section so that Vue knows to update the UI based on any changes. Changes to such variables flow through the Vue app logic so that anything that references the variable updates when the variable does.

Ok, I understand but I don't want to change (rendering) "screens" (update html with differente .vue content) from node-red. I want to change (rendering) them from Navigation Bar. I need more than tabs or cards, so, I want to load complete .vue contents. My "problem" is with navigation in html, not with pass values from node-red to vue files.

Trust me, you do :slight_smile:

Sorry, I probably over-simplified the explanation. The nav bar is rendered into a browser tab and, using Vue and javascript, is dynamically updated for the user. The result is that a part of the browser tab will indeed be changed - even if the user can't see that because they don't have the navbar open. The HTML still changes so that, when a user does open the navbar, it contains the correct menu.

By making the navbar something that Vue is going to render dynamically based on one of its responsive variables and the v-for directive, Vue will sort out everything for you. Once you have that, the navbar can contain any links you like and you can change them dynamically whenever you like - either based on data in the browser OR based on data coming from Node-RED, it makes no difference to Vue.

Now you are telling me something different though. You said you wanted to change the links in the navbar and that's what I've described.

If you want something more complex and want to split your code into separate .vue files, you can do that as well. You would use the .vue file to create a custom component and then use that component in your html.

However, I would strongly recommend getting the basics right first, you can always split some of your top-level code into components later but that is a large step-up in complexity.

And my explanation has been on how to do that. This is a Vue thing, not a Node-RED or uibuilder thing. If you want dynamic navigation bars, you need to use Vue to build the navbar from a variable as described. Then you can change the navbar simply by updating the variable, it does not matter at all what is driving change to the variable, only that it is changing.

Sorry, probably is my fault and dind't explain correctly but, for the first time, I only want to split my code into separate .vue files, without compiling (so is the reason for topic tittle:


I tried to do it with example from @afelix in github and I couldn't do it work, so, I decided to ask here.
I know is not a node-red thing. Only wanted to know if someone knows a way to do it.

I've done dashboard with tabs, cards, ... and now I wanted to go one step further.

hello .. you can study the code example of a bootstrap-vue / vue-router navigation bar i have on github. the difference is that i use a build step but you can try to modify it to work with httpVueLoader. hope you find some of it useful

Hi, Thanks a lot for the information. I'm going to take a look to that. Seems to be what I need.
Regards!

You should be able to use the http vue loader for this. I'm a bit lost though as to where you've got to.

Have you managed to get the basic http-vue-loader example working? If not, you need to sort that out first. Then doing a .vue for the navbar is trivial. Add 'navbar': httpVueLoader('navbar.vue'), to the components list, create your navbar.vue file with a suitable <template>. That's it.

Get the following working first:


My quick test. Note: change the location of http-vue-loader to the CDN version if you haven't installed it locally.

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>http-vue-loader example - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - http-vue-loader example">

    <link rel="icon" href="./images/node-blue.ico">

    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

</head><body>

    <div id="app" class="uib" v-cloak>
        <b-container id="app_container">

            <h1>http-vue-loader example - Node-RED and uibuilder</h1>

            <my-component :greeting="myGreeting" :who="myWho">
                <p>This text goes into the &lt;slot&gt; tags in the component. try removing this from index.html to see the default text.</p>
            </my-component>

            <pre id="msg" v-html="showLastReceivedMsg" class="syntax-highlight">Waiting for a message from Node-RED</pre>

        </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script>
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
    <!-- <script src="https://unpkg.com/http-vue-loader">/* Either load from CDN ... */</script> -->
    <script src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js">/* ... or from installed library */</script>
    <script src="./uibuilderfe.js"></script>
    <script src="./index.js"></script>

</body></html>

my-component.vue

<template>
    <b-row>
        <b-col class="hello" cols="12">
            {{greeting}} {{who}}
        </b-col>
        <slot>
            Default slot content, replace with your own text between the component tags.
            See the <a href="https://vuejs.org/v2/guide/components-slots.html" target="_blank">VueJS documentation for slots</a> for more information.
        </slot>
    </b-row>
</template>

<script>
module.exports = {
    /** Allow greeting and who to be passed as parameters but provide defaults
     * @see https://vuejs.org/v2/guide/components-props.html#Prop-Validation
     */
    props: {
        'greeting': {type: String, default: 'Hello'},
         'who': {type: String, default: 'World'},
    },

    /** Any data must be wrapped in a fn to isolate it in case this component is used multiple times */
    data: function() { return {

    }}
}
</script>

<style scoped>
.hello {
    background-color: #ffe;
    font-size: 2rem;
    text-align: center;
}
</style>

index.js

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
'use strict'

/** @see https://totallyinformation.github.io/node-red-contrib-uibuilder/#/front-end-library */

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    /** Load external component files
     *  Make sure there is no leading / in the name
     *  To load from the common folder use like: 'common/component-name.vue' */
    components: {
        'my-component': httpVueLoader('my-component.vue'),
    }, // --- End of components --- //

    data() { return {

        lastMsg    : '[Nothing]',

        myGreeting: 'Hi there',
        myWho: 'oh wise one!',

    }}, // --- End of data --- //

    computed: {

        /** Show the last msg from Node-RED nicely formatted */
        showLastReceivedMsg: function() {
            var lastMsg = this.lastMsg
            if (typeof lastMsg === 'string') return 'Last Message Received = ' + lastMsg
            else return 'Last Message Received = ' + this.syntaxHighlight(lastMsg)
        },

    }, // --- End of computed --- //

    methods: {

        // return formatted HTML version of JSON object
        syntaxHighlight: function(json) {
            json = JSON.stringify(json, undefined, 4)
            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
                var cls = 'number'
                if (/^"/.test(match)) {
                    if (/:$/.test(match)) {
                        cls = 'key'
                    } else {
                        cls = 'string'
                    }
                } else if (/true|false/.test(match)) {
                    cls = 'boolean'
                } else if (/null/.test(match)) {
                    cls = 'null'
                }
                return '<span class="' + cls + '">' + match + '</span>'
            })
            return json
        }, // --- End of syntaxHighlight --- //

    }, // --- End of methods --- //

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

    }, // --- End of created hook --- //

    /** Called once all Vue component instances have been loaded and the virtual DOM built */
    mounted: function(){

        const app = this  // Reference to `this` in case we need it for more complex functions

        // If msg changes - msg is updated when a standard msg is received from Node-RED
        uibuilder.onChange('msg', function(msg){
            app.lastMsg = msg

            // Change value
            if (msg.payload.hasOwnProperty('greeting')) app.myGreeting = msg.payload.greeting
            if (msg.payload.hasOwnProperty('who')) app.myWho = msg.payload.who
        })

    }, // --- End of mounted hook --- //

}) // --- End of app definition --- //

// EOF

Yes, I managed to get the basic http-vue-loader example. Only with CDN, maybe htt-vue-loader is not right installed in my device, but with CDN it works right. That's never been a problem.
The problem is I don't know how to make a dashboard with navigation, in wich there is a nav-bar or a simple nav and content changes clicking on it. Just like @UnborN example or like @afelix example. I tried it with:

  • [Preformatted text](https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Using-Vue-Router)
  • @UnborN code template [Preformatted text](https://github.com/unborn-andy/uib-template-vue-navbar)
  • [Preformatted text](https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Using-VueJS-with-Webpack)
  • Many other tries
    And I can't do it work.
    I can show vue file content with http-vue-loader example, but this is not a navigation site because content is fixed in screen.

You need to know where the .js file is - I show it in my previous example html file.

Right, so this example is a bit messy because I started trying to automate the creation of the multi-level navbar and I've not worked out all the kinks however, the basics work.

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>http-vue-loader example - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - http-vue-loader example">

    <link rel="icon" href="./images/node-blue.ico">

    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
    <link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

</head><body>

    <div id="app" class="uib" v-cloak>
        <b-container id="app_container">
            <navbar :routes="myRoutes"></navbar>

            <h1>http-vue-loader example - Node-RED and uibuilder</h1>

            <pre id="msg" v-html="showLastReceivedMsg" class="syntax-highlight">Waiting for a message from Node-RED</pre>
            
            <h2><router-view name="routeHeading" :key="$route.fullPath"></router-view></h2>
            <router-view :key="$route.fullPath"></router-view>
            
        </b-container>
    </div>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="../uibuilder/vendor/vue/dist/vue.js"></script>
    <!-- <script src="https://unpkg.com/http-vue-loader">/* Either load from CDN ... */</script> -->
    <script src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js">/* ... or from installed library */</script>
    <!-- <script src="../uibuilder/vendor/vuex/dist/vuex.js"></script> -->
    <script src="../uibuilder/vendor/vue-router/dist/vue-router.js"></script>
    <script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
    <script src="./uibuilderfe.js"></script>
    <script src="./index.js"></script>

</body></html>

index.js

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
'use strict'

const HeaderMenu = httpVueLoader('./components/HeaderMenu.vue');
const IndexView = httpVueLoader('./views/IndexView.vue');
const AdminView = httpVueLoader('./views/AdminView.vue');
const UsageInformation = httpVueLoader('./components/UsageInformation.vue');

const routeDefinitions = [
    {
        path: '/',
        name: 'index',
        title: 'Home', // This is a custom prop, used in the navbar
        components: {
            default: IndexView,
            routeHeading: HeaderMenu,
        },
    },
    {
        path: '/rooms',
        name: 'rooms_overview',
        title: 'Rooms',
        components: {
            default: httpVueLoader('./components/RoomsOverview.vue'),
            routeHeading: HeaderMenu,
        },
        children: [
            {
                path: 'rooms/:roomId',
                name: 'room',
                title: 'Room x',
                component: () => Promise.resolve(httpVueLoader('./components/Room.vue')),
            },
        ],
    },
    {
        path: '/admin',
        name: 'admin',
        title: 'Admin',
        component: UsageInformation,
    },
    {
        path: '/admin2',
        name: 'admin2',
        title: 'More Admin',
        components: {
            default: AdminView,
            routeHeading: HeaderMenu,
        },
        children: [
            {
                path: 'info',
                name: 'usage_info',
                component: httpVueLoader('./components/UsageInformation.vue'),
            }
        ]
    },
]

// eslint-disable-next-line no-unused-vars
const app = new Vue({
    el: '#app',

    // store: new Vuex.Store(store),
    router: new VueRouter({routes: routeDefinitions}),

    /** Load external component files
     *  Make sure there is no leading / in the name
     *  To load from the common folder use like: 'common/component-name.vue' */
    components: {
        'navbar': httpVueLoader('./components/navbar.vue'),
    }, // --- End of components --- //

    watch: {
        // Tell me if a route has changed
        '$route' (to, from) {
            console.log('Route changed from ' + from.path + ' to ' + to.path); 
        }
    },

    data() { return {

        lastMsg    : '[Nothing]',

        myGreeting: 'Hi there',
        myWho: 'oh wise one!',

        myRoutes: routeDefinitions, // redefine here so we can push it down

    }}, // --- End of data --- //

    computed: {

        /** Show the last msg from Node-RED nicely formatted */
        showLastReceivedMsg: function() {
            var lastMsg = this.lastMsg
            if (typeof lastMsg === 'string') return 'Last Message Received = ' + lastMsg
            else return 'Last Message Received = ' + this.syntaxHighlight(lastMsg)
        },

    }, // --- End of computed --- //

    methods: {

        // return formatted HTML version of JSON object
        syntaxHighlight: function(json) {
            json = JSON.stringify(json, undefined, 4)
            json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
                var cls = 'number'
                if (/^"/.test(match)) {
                    if (/:$/.test(match)) {
                        cls = 'key'
                    } else {
                        cls = 'string'
                    }
                } else if (/true|false/.test(match)) {
                    cls = 'boolean'
                } else if (/null/.test(match)) {
                    cls = 'null'
                }
                return '<span class="' + cls + '">' + match + '</span>'
            })
            return json
        }, // --- End of syntaxHighlight --- //

    }, // --- End of methods --- //

    /** Called after the Vue app has been created. A good place to put startup code */
    created: function() {

        uibuilder.start(this) // Single param passing vue app to allow Vue extensions to be used.

    }, // --- End of created hook --- //

    /** Called once all Vue component instances have been loaded and the virtual DOM built */
    mounted: function(){

        const app = this  // Reference to `this` in case we need it for more complex functions

        // If msg changes - msg is updated when a standard msg is received from Node-RED
        uibuilder.onChange('msg', function(msg){
            app.lastMsg = msg

            // Change value
            if (msg.payload.hasOwnProperty('greeting')) app.myGreeting = msg.payload.greeting
            if (msg.payload.hasOwnProperty('who')) app.myWho = msg.payload.who
        })

    }, // --- End of mounted hook --- //

}) // --- End of app definition --- //

// EOF

navbar.vue

<template>
    <b-navbar type="dark" variant="dark">
        <b-navbar-nav>
            <b-nav-text>
                <!-- <b-icon-lightning-fill animation="fade" style="animation-duration: 1.75s" scale="1.5" class="text-warning"></b-icon-lightning-fill> -->
                <span class="ml-3 mr-5 hvr-buzz-out">http-vue-loader</span>
            </b-nav-text>
            <template v-for="r in routes">
                <b-nav-item v-if="!r.children" active-class="active" class="nav-link" :to="r.path" :key="r.name">
                    {{ r.title }} 
                </b-nav-item>
                <b-nav-item-dropdown v-else :text="r.title" left :key="r.name">
                    <b-dropdown-item v-for="c in r.children" :to="c.path" :key="c.name" >
                        {{ c.title }}
                    </b-dropdown-item>
                </b-nav-item-dropdown>                
            </template>
            <!-- <b-nav-item to="/" exact :active="$route.name == 'home'">Home</b-nav-item>
            <b-nav-item to="/admin" exact :active="$route.name == 'admin'">Admin</b-nav-item>
            <b-nav-item to="/admin2" exact :active="$route.name == 'admin2'">More Admin</b-nav-item>
            <b-nav-item to="/rooms" exact :active="$route.name == 'rooms'">Room Overview</b-nav-item>
            <b-nav-item to="/page1" exact :active="$route.name == 'page1'">Page 1</b-nav-item>
            <b-nav-item to="/page2" exact :active="$route.name == 'page2'">Page 2</b-nav-item>
            <b-nav-item to="/page3" exact :active="$route.name == 'page3'">Page 3</b-nav-item>
            <b-nav-item-dropdown text="Page 4" left :class="{ active: $route.path.startsWith('/page4') }">
                <b-dropdown-item to="/page4a" exact :active="$route.name == 'page4a'">Page 4a</b-dropdown-item>
                <b-dropdown-item to="/page4b" exact :active="$route.name == 'page4b'">Page 4b</b-dropdown-item>
            </b-nav-item-dropdown> -->
        </b-navbar-nav>

        <!-- Right aligned nav items -->
        <b-navbar-nav class="ml-auto">
            <b-nav-item>
            <!-- v-b-tooltip.hover.left.v-danger="'Shutdown the device !'" -->
            <b-button
                size="sm"
                variant="danger"
                @click="$bvModal.show('modal-shutdown')"
                v-b-tooltip="{ trigger: 'hover', title: 'Shutdown the device', placement: 'left', variant: 'danger' }"
                ><!-- <b-icon icon="power"></b-icon -->
            </b-button>
            </b-nav-item>
        </b-navbar-nav>
        </b-navbar>
</template>

<script>
module.exports = {
    props: {
        'routes': {type: Array, default: {}},
    },
}
</script>

You will also need to create the .vue files referenced in the index.js file. These can all contain just a <template> tag with some random content.

Oh, and one word of warning - when defining the html, don't use the empty tag syntax like <navbar /> because that doesn't work for some reason, you have to use the full syntax <navbar></navbar>.

Thanks for your help and patience. I think I'm going to quit and go back to node-red dashboard. I'm not able to get this. I'm not able to show any .vue template content in main html


I can't see nothing.

I've created .vue files and folders and I can't do it work.
Regards

.... ummm... I think the problem was in vue-router version... I got v4 and couldn't use VueRouter() in index.js. All crash...
I've uninstalled it and install v3. Now, when I use VueRouter() not crash.
@TotallyInformation , Can you sare HeaderMenu.vue or IndexView.vue or Room.vue or anyother .vue file to see how is structured?
Regards

Appologies, I thought I'd mentioned versions but perhaps not as I'm in multiple conversations at the moment.

Yes, one of the issues with Vue at present is that they decided to switch their default version from v2 to v3 and vue-router did the same but confusingly vue-router v4 is the one that supports Vue v3!

You should have got an error when installing vue-router v4 (as I did) because it won't install with vue v2. However, maybe it was because I was using uibuilder vNext which has much better error reporting.

There is no difference between the components and views, I just picked these out of one of the other examples and made them work. They all take the same form as in this example of HeaderMenu.vue:

<template>
    <p>My menu</p>
</template>

:grinning:

Thanks again for help. At least, I got a navbar and navigation between .vue files! but... Now, I'm "fighting" with pass data from node-red to .vue files and vice-versa :sweat_smile:
In vue-file example it's clear,

              <my-component :greeting="myGreeting" :who="myWho"> </p>
        </my-component>`

because, "is fixed" in html code but using vue-route ....

   <router-view: key="$route.fullPath">
   </router-view>