-
-
Notifications
You must be signed in to change notification settings - Fork 86
Using Vue Router
UPDATE 2023: The below uses the old client - this still works but you will miss out on the new features. Also, take note that if you are using Vue v2 as in this example, you have to use vue-router v3 - yes, you can't make this stuff up! The vue-router documentation doesn't tell you how to install the correct version.
UPDATE 2022: Please make sure that you are using vuex@2 and vue-router@2 - you need to specify the versions because both libraries have changed their defaults to work with VueJS v3 and you will most likely still be working with VueJS v2.
Reference this discussion on the Node-RED Forum.
AFelix provided a detailed example:
I've successfully used Vue Router without a build step in Uibuilder before.
Let me see if I can link the files here for my demo that uses both Vue router and VueX.
Single node uibuilder on /demo
:
[{"id":"dbb0bd8f.7242a","type":"uibuilder","z":"fe5232bf.c0c7f","name":"","topic":"","url":"demo","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":false,"x":170,"y":680,"wires":[[],[]]}]
With 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">
<!-- Self closing tags in general WON'T work with http-vue-loader!!! -->
<router-view name="menu"></router-view>
<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="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></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>
With index.js
:
'use strict'
import store from './store.js';
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, '&').replace(/</g, '<').replace(/>/g, '>')
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 --- //
store: new Vuex.Store(store),
router: new VueRouter(router),
}) // --- End of app1 --- //
// EOF
With router.js
:
const HeaderMenu = httpVueLoader('./components/HeaderMenu.vue');
const IndexView = httpVueLoader('./views/IndexView.vue');
const AdminView = httpVueLoader('./views/AdminView.vue');
export default {
routes: [
{
path: '/',
name: 'index',
components: {
default: IndexView,
menu: HeaderMenu,
},
},
{
path: '/rooms',
name: 'rooms_overview',
components: {
default: httpVueLoader('./components/RoomsOverview.vue'),
menu: HeaderMenu,
},
children: [
{
path: 'rooms/:roomId',
name: 'room',
component: () => Promise.resolve(httpVueLoader('./components/Room.vue')),
},
],
},
{
path: '/admin',
name: 'admin',
components: {
default: AdminView,
menu: HeaderMenu,
},
children: [
{
path: 'info',
name: 'usage_info',
component: httpVueLoader('./components/UsageInformation.vue'),
}
]
},
],
};
With store.js
:
import rooms from './store-modules/rooms.js';
import devices from './store-modules/devices.js';
export default {
state: {
unconfiguredRooms: [],
unconfiguredDevices: [],
},
getters: {
hasUnconfiguredItems: state => state.unconfiguredRooms.length !== 0 || state.unconfiguredDevices.length !== 0,
},
mutations: {
},
actions: {
},
modules: {
rooms,
devices,
},
};
And a few more files not at top level but I'm not connected to the machine atm so I can't easily link those, but that should give you an idea. If I remember I'll put this in a standalone demo flow for the flows library.
Please feel free to add comments to the page (clearly mark with your initials & please add a commit msg so we know what has changed). You can contact me in the Discourse forum, or raise an issue here in GitHub! I will make sure all comments & suggestions are represented here.
-
Walkthrough 🔗 Getting started
-
In Progress and To Do 🔗 What's coming up for uibuilder?
-
Awesome uibuilder Examples, tutorials, templates and references.
-
How To
- How to send data when a client connects or reloads the page
- Send messages to a specific client
- Cache & Replay Messages
- Cache without a helper node
- Use webpack to optimise front-end libraries and code
- How to contribute & coding standards
- How to use NGINX as a proxy for Node-RED
- How to manage packages manually
- How to upload a file from the browser to Node-RED
-
Vanilla HTML/JavaScript examples
-
VueJS general hints, tips and examples
- Load Vue (v2 or v3) components without a build step (modern browsers only)
- How to use webpack with VueJS (or other frameworks)
- Awesome VueJS - Tips, info & libraries for working with Vue
- Components that work
-
VueJS v3 hints, tips and examples
-
VueJS v2 hints, tips and examples
- Dynamically load .vue files without a build step (Vue v2)
- Really Simple Example (Quote of the Day)
- Example charts using Chartkick, Chart.js, Google
- Example Gauge using vue-svg-gauge
- Example charts using ApexCharts
- Example chart using Vue-ECharts
- Example: debug messages using uibuilder & Vue
- Example: knob/gauge widget for uibuilder & Vue
- Example: Embedded video player using VideoJS
- Simple Button Acknowledgement Example Thanks to ringmybell
- Using Vue-Router without a build step Thanks to AFelix
- Vue Canvas Knob Component Thanks to Klaus Zerbe
-
Examples for other frameworks (check version before trying)
- Basic jQuery example - Updated for uibuilder v6.1
- ReactJS with no build - updated for uibuilder v5/6
-
Examples for other frameworks (may not work, out-of-date)
-
Outdated Pages (Historic only)
- v1 Examples (these need updating to uibuilder v2/v3/v4/v5)