-
-
Notifications
You must be signed in to change notification settings - Fork 86
Dynamically load .vue files without a build step
See also the WIKI article Load Vue components without a build step (modern browsers only) for an alternative approach.
NOTE that http-vue-loader is for VueJS v2 only. For v3, try vue3-sfc-loader.
Note: If you want to use http-vue-loader locally rather than from a CDN. Install it using uibuilder's library manager and load it using the following statement in your index.html: <script src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></script>
.
Many front-end frameworks require you to have a build step (using webpack or similar) in order to put everything together in a way that will work in all browsers.
Thankfully, VueJS is quite flexible in this sense and, assuming that you load the full version of vue that includes the compiler, you compile components on the fly.
Unfortunately, this can, however, quickly get very messy and confusing - having everything in one javascript file for example. VueJS has the concept of single file components that greatly helps you to break complex web apps into manageable and reusable chunks.
However, that brings us back to having to use a build step again. Except that someone clever has built a small utility that lets us dynamically load .vue
files without a build step. This is called http-vue-loader.
The example give here shows you how to use http-vue-loader and also shows you the basics of using a Vue single file component.
This example loads http-vue-loader from a CDN over the Internet but also shows you how to use a version installed using npm install http-vue-loader
in your userDir. You can, of course, use the uibuilder v2 configuration panel in the Editor to install this.
Note that the example flow includes 3 comment nodes containing the code for the 3 files. Copy the code and paste into the appropriate files using the uibuilder code editor - you will need to create the new my-component.vue
file in the src
folder.
[{"id":"df81cc9e.a9171","type":"inject","z":"18cb249f.38bafb","name":"repeat every 10s","topic":"","payload":"","payloadType":"date","repeat":"10","crontab":"","once":false,"onceDelay":0.1,"x":130,"y":80,"wires":[["6ca36476.d0597c"]]},{"id":"76678044.5aaeb","type":"debug","z":"18cb249f.38bafb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":60,"wires":[]},{"id":"bd9b5761.fb4918","type":"debug","z":"18cb249f.38bafb","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","x":730,"y":100,"wires":[]},{"id":"b18a50dd.f7e5c","type":"uibuilder","z":"18cb249f.38bafb","name":"","topic":"","url":"vue-file","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"showfolder":true,"x":500,"y":80,"wires":[["76678044.5aaeb"],["bd9b5761.fb4918"]]},{"id":"6ca36476.d0597c","type":"change","z":"18cb249f.38bafb","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"{\"greeting\":\"Goodness gracious me\",\"who\":\"little fishy\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":80,"wires":[["b18a50dd.f7e5c"]]},{"id":"3ebfbce9.6ccd84","type":"comment","z":"18cb249f.38bafb","name":"index.html","info":"copy the following and paste into `index.html`\n\n```html\n<!doctype html><html lang=\"en\"><head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes\">\n\n <title>http-vue-loader/VueJS Example - Node-RED UI Builder</title>\n <meta name=\"description\" content=\"Node-RED UI Builder - http-vue-loader/VueJS Example\">\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\n <link rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n</head><body>\n <div id=\"app\">\n <b-container id=\"app_container\">\n\n <h1>\n Example of loading a .vue file\n </h1>\n\n <p>\n Uses <a href=\"https://github.com/FranckFreiburger/http-vue-loader#readme\" target=\"_blank\">http-vue-loader</a>\n in order to avoid a build step. Also see the\n <a href=\"https://vuejs.org/v2/guide/single-file-components.html\" target=\"_blank\">Vue documentation on single file components</a>.\n </p>\n <p>\n This example uses a custom VueJS component called <my-component> which is defined in the file <code>my-compontent.vue</code>.\n </p>\n <p>\n The custom component has two \"props\" (properties) that allow you to send data to it. These override the default greeting and name.\n By passing <code>msg.payload.greeting</code> and/or <code>msg.payload.who</code> in the msg from Node-RED, you will see the text\n change on-screen dynamically.\n </p>\n\n <my-component :greeting=\"myGreeting\" :who=\"myWho\">\n <p>This text goes into the <slot> tags in the component. try removing this from index.html to see the default text.</p>\n </my-component>\n\n </b-container>\n </div>\n\n <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n <!-- Use vue.min.js for production use -->\n <script src=\"../uibuilder/vendor/vue/dist/vue.js\"></script>\n <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js\"></script>\n\n <!-- Loading from CDN -->\n <script src=\"https://unpkg.com/http-vue-loader\"></script>\n <!-- Loading from npm installed version -->\n <!-- <script src=\"../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js\"></script> -->\n\n <script src=\"./uibuilderfe.min.js\"></script> <!-- //prod version -->\n <script src=\"./index.js\"></script>\n\n</body></html>\n```","x":100,"y":40,"wires":[]},{"id":"7254bbb9.903ce4","type":"comment","z":"18cb249f.38bafb","name":"index.js","info":"Copy the following and paste into `index.js`\n\n```javascript\n/* jshint browser: true, esversion: 5 */\n/* globals document,Vue,window,uibuilder,httpVueLoader */\n// @ts-nocheck\n/*\n Copyright (c) 2019 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://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */\n\n// eslint-disable-next-line no-unused-vars\nvar app1 = new Vue({\n el: '#app',\n\n /** Load external component files\n * Make sure there is no leading / in the name\n * To load from the common folder use like: 'common/component-name.vue' */\n components: {\n 'my-component': httpVueLoader('my-component.vue'),\n }, // --- End of components --- //\n\n data: {\n myGreeting: 'Hi there',\n myWho: 'oh wise one!',\n }, // --- End of data --- //\n\n computed: {\n }, // --- End of computed --- //\n\n methods: {\n }, // --- End of methods --- //\n\n // Available hooks: init,mounted,updated,destroyed\n mounted: function(){\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('/nr/uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names\n */\n uibuilder.start()\n\n var vueApp = this\n\n // Process new messages from Node-RED\n uibuilder.onChange('msg', function (msg) {\n // We are assuming that msg.payload is an number between zero and 10\n\n // Change value\n if (msg.payload.hasOwnProperty('greeting')) vueApp.myGreeting = msg.payload.greeting\n if (msg.payload.hasOwnProperty('who')) vueApp.myWho = msg.payload.who\n\n //console.log(msg)\n })\n\n } // --- End of mounted hook --- //\n\n}) // --- End of app1 --- //\n\n// EOF\n```\n","x":230,"y":40,"wires":[]},{"id":"4f7ddf94.c4dde","type":"comment","z":"18cb249f.38bafb","name":"my-component.vue","info":"Copy the following and paste into `my-component.vue`\n\n```\n<template>\n <b-row>\n <b-col class=\"hello\" cols=\"12\">\n {{greeting}} {{who}}\n </b-col>\n <slot>\n Default slot content, replace with your own text between the component tags.\n See the <a href=\"https://vuejs.org/v2/guide/components-slots.html\" target=\"_blank\">VueJS documentation for slots</a> for more information.\n </slot>\n </b-row>\n</template>\n\n<script>\nmodule.exports = {\n /** Allow greeting and who to be passed as parameters but provide defaults\n * @see https://vuejs.org/v2/guide/components-props.html#Prop-Validation\n */\n props: {\n 'greeting': {type: String, default: 'Hello'},\n 'who': {type: String, default: 'World'},\n },\n\n /** Any data must be wrapped in a fn to isolate it in case this component is used multiple times */\n data: function() { return {\n\n }}\n}\n</script>\n\n<style scoped>\n.hello {\n background-color: #ffe;\n font-size: 2rem;\n text-align: center;\n}\n</style>\n```\n","x":390,"y":40,"wires":[]}]
<!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>http-vue-loader/VueJS Example - Node-RED UI Builder</title>
<meta name="description" content="Node-RED UI Builder - http-vue-loader/VueJS 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 rel="stylesheet" href="./index.css" media="all">
</head><body>
<div id="app">
<b-container id="app_container">
<h1>
Example of loading a .vue file
</h1>
<p>
Uses <a href="https://github.com/FranckFreiburger/http-vue-loader#readme" target="_blank">http-vue-loader</a>
in order to avoid a build step. Also see the
<a href="https://vuejs.org/v2/guide/single-file-components.html" target="_blank">Vue documentation on single file components</a>.
</p>
<p>
This example uses a custom VueJS component called <my-component> which is defined in the file <code>my-compontent.vue</code>.
</p>
<p>
The custom component has two "props" (properties) that allow you to send data to it. These override the default greeting and name.
By passing <code>msg.payload.greeting</code> and/or <code>msg.payload.who</code> in the msg from Node-RED, you will see the text
change on-screen dynamically.
</p>
<my-component :greeting="myGreeting" :who="myWho">
<p>This text goes into the <slot> tags in the component. try removing this from index.html to see the default text.</p>
</my-component>
</b-container>
</div>
<script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
<!-- Use vue.min.js for production use -->
<script src="../uibuilder/vendor/vue/dist/vue.js"></script>
<script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
<!-- Loading from CDN -->
<script src="https://unpkg.com/http-vue-loader"></script>
<!-- Loading from npm installed version -->
<!-- <script src="../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js"></script> -->
<script src="./uibuilderfe.min.js"></script> <!-- //prod version -->
<script src="./index.js"></script>
</body></html>
/* jshint browser: true, esversion: 5 */
/* globals document,Vue,window,uibuilder,httpVueLoader */
// @ts-nocheck
/*
Copyright (c) 2019 Julian Knight (Totally Information)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict'
/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */
// eslint-disable-next-line no-unused-vars
var app1 = 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: {
myGreeting: 'Hi there',
myWho: 'oh wise one!',
}, // --- End of data --- //
computed: {
}, // --- End of computed --- //
methods: {
}, // --- End of methods --- //
// Available hooks: init,mounted,updated,destroyed
mounted: function(){
/** **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
// Process new messages from Node-RED
uibuilder.onChange('msg', function (msg) {
// We are assuming that msg.payload is an number between zero and 10
// Change value
if (msg.payload.hasOwnProperty('greeting')) vueApp.myGreeting = msg.payload.greeting
if (msg.payload.hasOwnProperty('who')) vueApp.myWho = msg.payload.who
//console.log(msg)
})
} // --- End of mounted hook --- //
}) // --- End of app1 --- //
// EOF
<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>
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)