One of the reasons I no longer use any frameworks.
Here is some code that should work I think. Though I think that it is using Vue v3 so if you have Vue v2 installed, switch the vue library script line to the commented out version to load from CDN to save you messing about.
index.html
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Simple Vue3 with new uib client - Node-RED uibuilder</title>
<meta name="description" content="Node-RED uibuilder - Simple Vue3 with new uib client">
<link rel="icon" href="./images/node-blue.ico">
<!-- Your own CSS -->
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
<!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / - socket.io no longer needed -->
<!-- <script defer src="https://cdn.jsdelivr.net/npm/vue@latest/dist/vue.global.prod.js">/* Remove .prod for development version */</script> -->
<script defer src="../uibuilder/vendor/vue/dist/vue.global.prod.js">/* Remove .prod for development version */</script>
<script defer src="../uibuilder/vendor/vue3-sfc-loader/dist/vue3-sfc-loader.js"></script>
<script defer src="../uibuilder/uibuilder.iife.min.js"></script>
<script defer src="./index.js">/* OPTIONAL: Put your custom code in that */</script>
<!-- #endregion -->
</head><body class="uib">
<div id="app">
<h1>Simple Vue v3 App Using New uibuilder IIFE Client - v6.1.0</h1>
<p>{{ message }}</p>
<div id="more"><!-- '#more' is used as a parent for dynamic content in examples --></div>
<div style="border:2px solid silver;">
<my-component color="yellow"></my-component>
</div>
<p>
<button @click="count++" title="Not sent back to Node-RED - this just shows that VueJS is working">
Simple VueJS Counter: {{ count }}
</button>
</p>
<p>
<button @click="doEvent" data-mything="This is a thing" title="This is sent back to Node-RED">
Click to send msg to Node-RED
</button>
</p>
<p>
<label for="input1">This is sent to Node-RED when you exit it: </label>
<input v-model="input1" @change="doInputChange" id="input1" type="text" placeholder="Type text then exit field">
</p>
<pre id="msg" v-html="formatLastRcvd" class="syntax-highlight">Waiting for a message from Node-RED</pre>
</div>
</body></html>
index.js
// @ts-nocheck
'use strict'
const { createApp } = Vue
const { loadModule } = window["vue3-sfc-loader"]
// See vue3-sfc-loader docs for details.
const sfcOptions = {
moduleCache: {
vue: Vue,
},
getFile(url) {
return fetch(url).then(response => response.ok ? response.text() : Promise.reject(response));
},
addStyle(styleStr) {
const style = document.createElement('style');
style.textContent = styleStr;
const ref = document.head.getElementsByTagName('style')[0] || null;
document.head.insertBefore(style, ref);
},
log(type, ...args) {
console.log(type, ...args);
}
}
// Using the Vue options API style for beginner simplicity
// No need to pre-define Quasar's $q when working with the options API
const app = createApp({
components: {
'my-component': Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', sfcOptions)),
},
// Define Vue reactive variables
data() {
return {
message: 'Hello Vue!',
count: 0,
lastMsgRecvd: '[Nothing]',
input1: '',
}
},
// Dynamic data
computed: {
// This is auto-recalculated by Vue when lastMsgRecvd changes
formatLastRcvd() {
const lastMsgRecvd = this.lastMsgRecvd
if (typeof lastMsgRecvd === 'string') return 'Last Message Received = ' + lastMsgRecvd
return 'Last Message Received = ' + uibuilder.syntaxHighlight(lastMsgRecvd)
},
},
// Supporting functions
methods: {
// Use the uib helper function to send something to NR
doEvent(event) { uibuilder.eventSend(event) },
/** Runs when the change event for the source field is fired
* @param {InputEvent} event The event object is passed by the browser automatically
*/
doInputChange(event) {
console.log('input1 text has changed: ', event.target.value)
// Send the new text to Node-RED
uibuilder.send({
'topic': 'input1-changed',
'payload': event.target.value,
})
},
},
// Lifecycle hooks
mounted() {
// If msg changes - msg is updated when a standard msg is received from Node-RED
uibuilder.onChange('msg', (msg) => {
console.log('>> msg recvd >>', msg, this)
this.lastMsgRecvd = msg
})
},
})
app.mount('#app')
myComponent.vue
<template>
<div>
<p>
The VueJS component called <a href="https://github.com/FranckFreiburger/vue3-sfc-loader" target="_blank">vue3-sfc-loader</a>
provides the ability to dynamically load a Vue component from a <code>.vue</code> file without the need for a pre-build step.
</p>
<P>
It is worth noting that this also works with VueJS v2.
See the <a href="https://github.com/FranckFreiburger/vue3-sfc-loader/blob/main/docs/examples.md#vue2-basic-example" target="_blank">docs</a> for details.
</P>
<p>
This example uses a dynamically loaded <span class="example">{{ msg }}</span> file!
</p>
</div>
</template>
<style scoped>
.example {
color: v-bind('color');
}
p::before {
content: "VUE file content: ";
font-variant-caps: small-caps;
font-style: italic;
}
</style>
<script>
export default {
data() {
return {
msg: 'myComponent.vue',
color: 'red',
}
}
}
</script>