Switching pages using Vue3 routing with uibuilder

This is an example about using Vue3 routing mechanism on uibuilder v.5.1.1, with the new uibuilder.iife library.

The example is aimed to show 2 different scenarios:

  • changing the route view from the app, by clicking on the buttons;
  • changing the route view from a Node Red flow, by injecting a message with a topic of router/switch and the route path in msg.payload. This can be useful to switch the Dashboard on a specific page when an important event happens in the flows.

The route files (.vue) are stored in the folder: ./routes/

index.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Router test - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - Router test">
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">
</head>

<body>
    <div id="my-app" style="text-align:center">
        <nav>
            <router-link to='/'><button style="margin-right:3em">Home</button></router-link>
            <router-link to='/contacts'><button>Contacts</button></router-link>
        </nav>
        <router-view v-slot="{ Component }">
            <keep-alive>
                <component :is="Component" />
            </keep-alive>
        </router-view>
    </div>

    <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.global.js"></script>
    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <script src="../uibuilder/uibuilder.iife.min.js"></script>
    <script src="./index.js"></script>
</body>
</html>

index.js


'use strict'
const { createApp, ref } = Vue
const options = {
    moduleCache: {
        vue: Vue,
        vueRouter: VueRouter
    },
    async getFile(url) {
        const res = await fetch(url);
        if (!res.ok)
            throw Object.assign(new Error(res.statusText + ' ' + url), { res });
        return {
            getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
        }
    },
    addStyle(textContent) {

        const style = Object.assign(document.createElement('style'), { textContent });
        const ref = document.head.getElementsByTagName('style')[0] || null;
        document.head.insertBefore(style, ref);
    },
}

const { loadModule } = window['vue3-sfc-loader'];

window.asyncVue = sfc => Vue.defineAsyncComponent(() => loadModule('./routes/' + sfc + '.vue', options))
window.dynamicVue = com => ({
    name: 'DynamicWrapper',
    template: `<component :is="comp"></component>`,
    computed: {
        comp() {
            const com = this.com;
            return asyncVue(com);
        }
    },
    data() {
        return { com }
    }
})

const routes = [
    { path: "/", name: 'home', component: dynamicVue('home'), meta: { KeepAlive: true } },
    { path: "/contacts", component: dynamicVue('contacts'), meta: { KeepAlive: true } },

];

const router = VueRouter.createRouter({
    history: VueRouter.createWebHashHistory(),
    routes
})


const app = Vue.createApp({
    data() {
        return {
        }
    },
    methods: {
        switchToRoute(rt) {
            this.$router.push(rt)
        }
    },
    mounted: function () {
        uibuilder.onChange('msg', (msg) => {
            if (msg.topic === "router/switch") { this.switchToRoute(msg.payload); }
        })
    },
})

app.use(router)
app.mount('#my-app')

home.vue

<template>
    <h4> This is Home Page</h4>
    <p class="blueclass">Router view</p>
    <p>{{ local }}</p>
</template>

<script>
export default {

    data() {
        return {
            local: "This variable is local to the Home view"
        }
    },
}
</script>

<style scoped>
.blueclass {
    color: blue;
}
</style>

contacts.vue

<template>
<h4> This is the Contacts Page</h4>
<p class="greenclass">Router view</p>
<p>{{local}}</p>
</template>

<script>
export default {

    data() {
        return {
            local: "This variable is local to the Contacts view"
        }
    }
}
</script>

<style scoped>
.greenclass {
    color: green;
}
</style>
[{"id":"203435ce1f57e6cd","type":"inject","z":"bfa02725ef845ede","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"router/switch","payload":"/","payloadType":"str","x":274,"y":460,"wires":[["296594508ac2e284"]]},{"id":"81ff8800034014d3","type":"inject","z":"bfa02725ef845ede","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"router/switch","payload":"/contacts","payloadType":"str","x":250,"y":540,"wires":[["296594508ac2e284"]]},{"id":"296594508ac2e284","type":"uibuilder","z":"bfa02725ef845ede","name":"","topic":"","url":"router-test","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"blank","extTemplate":"","showfolder":false,"reload":false,"sourceFolder":"src","deployedVersion":"5.1.1","x":470,"y":489,"wires":[[],[]]}]

Flow Info

Created 1 year, 11 months ago
Rating: 5 2

Owner

Actions

Rate:

Node Types

Core
  • inject (x2)
Other
  • uibuilder (x1)

Tags

  • uibuilder
  • Vue3
  • routing
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option