It can be if you aren't familiar with web development, there are a lot of things to learn.
The main thing to remember is that uibuilder iteself is very simple to use. But it's main purpose is to act as a link between the data you throw about in Node-RED and a users browser (where the UI lives). If you can keep that straight, it will really help you as you progress.
So lets break this down. There are three things you are trying to achieve.
- Get some data
- Show that data in a chart.
- Save that data to a DB for later use every 5 minutes.
Actually, only step 2 might use uibuilder
There isn't any point in trying to save the data "from the chart". Because Node-RED can already help you with that and it already has the data. The only reason to get uibuilder involved with step 3 would be if you wanted the USER of the chart to select what data to save. If you just want it saving every 5 minutes, get Node-RED to do that, it is what it is good at and will be a lot simpler than trying to send it to the browser and then send it back (which is where uibuilder is really doing the work).
Now, of course, step 2 is a little more complex than you initially state because actually you want the chart to update whenever new data comes along. But the Vega library may be able to do this without VueJS (we'll come back to VueJS in a moment, bear with me). This example shows you a simple web page that updates a chart from data sent over a websocket connection. Vega-Lite with Websockets - bl.ocks.org
If you look at the script embedded in the html file, you can see where it creates a websocket and listens for data over it. This is what uibuilder sets up for you. Instead of conn.onmessage = function(event) {
, you would have uibuilder.onChange('msg', function(msg){
and instead of event.data
, you would simply use msg
. The part about cennting to the echo server and the conn.onopen
isn't needed at all since uibuilder takes care of all the details for you.
I use that as an example that I hope shows that VueJS is not really needed for simple things. However, it becomes really helpful as the complexity of your web app grows.
First thing to check is that you understand there are differences between Vue v2 and v3? Stick with v2 for now as not everything is yet compatible with v3. If you are following a tutorial, it should tell you which version it is targetting.
Next, here is a QA about this topic: vue.js - Difference between .$mount() and el [Vue JS] - Stack Overflow
The answer to your question is: not a lot! All it means is that you get to choose when you create the Vue app. In by far the majority of cases, you want the app created as early as possible and so the const app = new Vue({...})
version is going to be the simplest anyway.
OMG!!! I've just managed to find some time to try and use Vega for the first time - it is HORRIBLE!!
Just a quick check ApexChart is obviously not the same as Vega - so your examples don't quite align. ApexChart is much easier to use but probably not as powerful.
The component is likely to be better for you if you are going to be creating something more complex that will benefit from a build step (using webpack, parcel or similar). Annoyingly, all the examples for vue-vega assume that you will be using a build step to "compile" the code. Example: Vue - CodeSandbox I've not managed to find any that let you load things directly.
As mentioned, apexchart isn't the same as Vega.
All that example is doing is pushing new data onto the end of the series that apexchart is showing. But, at the same time, it is dropping off the first element of the array after the new element is added so that you don't end up with an infinite number of elements in the array which would eventually crash the browser tab. push
and shift
are JavaScript array functions.
Right, so a quick and nasty Vega example - without VueJS for now:
[{"id":"06557b5a92be6c25","type":"inject","z":"b533fe741719fa7a","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{ \"a\": \"j\", \"b\": 70 }","payloadType":"json","x":240,"y":220,"wires":[["e0e67a1f4275c9a0"]]},{"id":"e0e67a1f4275c9a0","type":"uibuilder","z":"b533fe741719fa7a","name":"","topic":"","url":"vega","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"blank","extTemplate":"","showfolder":false,"useSecurity":false,"allowUnauth":false,"allowAuthAnon":false,"sessionLength":432000,"tokenAutoExtend":false,"reload":true,"sourceFolder":"src","deployedVersion":"5.0.0-dev","x":430,"y":220,"wires":[[],[]]}]
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, initial-scale=1">
<title>Node-RED UI Builder - Blank template</title>
<meta name="description" content="Node-RED UI Builder - Blank template">
<link rel="icon" href="./images/node-blue.ico">
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
</head><body>
<h1>uibuilder Vega Simple Example</h1>
<div id="view"></div>
<pre id="msg" class="syntax-highlight">Waiting for a message from Node-RED</pre>
<script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vega@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
<script src="https://cdn.jsdelivr.net/npm/vega-embed@6"></script>
<script src="./uibuilderfe.min.js"></script>
<script src="./index.js"></script>
</body></html>
index.js
/* eslint-disable strict */
/* jshint browser: true, esversion: 6, asi: true */
/* globals uibuilder */
// @ts-nocheck
/** Minimalist code for uibuilder and Node-RED */
'use strict'
// return formatted HTML version of JSON object
window.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 --- //
// run this function when the document is loaded
window.onload = function() {
// Start up uibuilder - see the docs for the optional parameters
uibuilder.start()
const origData = [
{ "a": "A", "b": 28 }, { "a": "B", "b": 55 }, { "a": "C", "b": 43 },
{ "a": "D", "b": 91 }, { "a": "E", "b": 81 }, { "a": "F", "b": 53 },
{ "a": "G", "b": 19 }, { "a": "H", "b": 87 }, { "a": "I", "b": 52 }
]
const vlJson = {
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
//"values": origData
"name": 'myData'
},
"mark": "bar",
"encoding": {
"x": { "field": "a", "type": "nominal", "axis": { "labelAngle": 0 } },
"y": { "field": "b", "type": "quantitative" }
}
}
// Embed visualization and save view as window.view:
vegaEmbed('#view', vlJson).then( res => {
res.view
.insert('myData', origData)
.run()
// Save the view to window.view for later use
window.view = res.view
})
//console.log(vega.changeset())
// Listen for incoming messages from Node-RED
uibuilder.onChange('msg', function(msg){
console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
// dump the msg as text to the "msg" html element
const eMsg = document.getElementById('msg')
eMsg.innerHTML = window.syntaxHighlight(msg)
var changeSet = vega
.changeset()
.insert(msg.payload)
// .remove(function (t) {
// return t.x < minimumX
// })
window.view.change('myData', changeSet).run().resize()
// Changeset needs to remove everything first, then insert new data
// let changeset = vega.changeset().remove(() => true).insert(data);
// For some reason source_0 is the default dataset name
// view.change('source_0', changeset).run()
})
}
This is based on the Blank template. It loads a simple bar chart with data defined in the js file and then allows you to load more data from the flow. I've only added a single new data item j
, you'll need to amend somewhat if you want to keep adding things, at the moment, if you keep inserting the data, column j
just keeps growing.
It is clear from this example that using the vue-vega component will be benefitial. It takes care of all of the c**p for you.