-
-
Notifications
You must be signed in to change notification settings - Fork 86
Integrating ApexCharts with VueJS
UPDATE 2022-12-21 - Updated the code to use the latest uibuilder v6 FE library. Also added a Line chart with expanding data and updated both the Line and Bar chart to update correctly. Noting that ApexChart uses far too complex a data object in many cases and this does not play nicely with VueJS. This means having to jump through hoops to get the chart to update dynamically. Also, while the ApexChart examples on their website look comprehensive - they have far too many missing pieces that make learning much too hard for beginners.
UPDATE 2021-03-01 - I've had reports that ApexCharts doesn't work with Android Chrome or Android Firefox. But I've no idea how true that is.
Here is an example of how to use ApexCharts with the default uibuilder v2 installation to add a simple chart.
Add an instance of uibuilder to your flows and replace the index.html and index.js files with the code below.
I'm loading the chart libraries using a CDN here, the next step would be to install the two packages using uibuilder's npm interface.
The example flow sends a random number to the app every 5 seconds which is used to update the charts.
[
{
"id": "6814d51fcda06a50",
"type": "inject",
"z": "2712d438cfda4c65",
"name": "",
"props": [
{
"p": "payload"
}
],
"repeat": "5",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "($random()*10)+($random()*10)",
"payloadType": "jsonata",
"x": 140,
"y": 80,
"wires": [
[
"f12c790f580f771b"
]
]
},
{
"id": "f12c790f580f771b",
"type": "uibuilder",
"z": "2712d438cfda4c65",
"name": "",
"topic": "",
"url": "apex-charts",
"fwdInMessages": false,
"allowScripts": false,
"allowStyles": false,
"copyIndex": true,
"templateFolder": "vue",
"extTemplate": "",
"showfolder": false,
"reload": false,
"sourceFolder": "src",
"deployedVersion": "6.1.0",
"showMsgUib": false,
"x": 330,
"y": 80,
"wires": [
[
"f0d9177615199688"
],
[
"a98558a0e03506ef"
]
]
},
{
"id": "f0d9177615199688",
"type": "debug",
"z": "2712d438cfda4c65",
"name": "debug 34",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "counter",
"x": 495,
"y": 60,
"wires": [],
"l": false
},
{
"id": "a98558a0e03506ef",
"type": "debug",
"z": "2712d438cfda4c65",
"name": "debug 35",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": true,
"complete": "true",
"targetType": "full",
"statusVal": "",
"statusType": "counter",
"x": 495,
"y": 120,
"wires": [],
"l": false
}
]
<!doctype html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>ApexCharts/VueJS/bootstrap-vue Example - Node-RED uibuilder</title>
<meta name="description" content="Node-RED uibuilder - ApexCharts/VueJS/bootstrap-vue 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" />
<!-- Your own CSS -->
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
</head><body>
<div id="app" class="uib" v-cloak><!-- All UI code needs to be in here -->
<b-container id="app_container"><!-- Wraps the bootstrap-vue formatting -->
<b-img src="./images/node-blue-192x192.png" rounded left v-bind="imgProps" alt="Blue Node-RED" class="mt-1 mr-2"></b-img>
<h1>ApexCharts/VueJS/bootstrap-vue Example</h1>
<p>
Examples of using <a href="https://apexcharts.com/docs/vue-charts/" target="_blank">ApexChart</a>
with uibuilder, VueJS and bootstrap-vue.
</p>
<p>
Note that ApexCharts uses very complex data objects that VueJS can struggle with.
See the notes in <code>index.js</code>
</p>
<b-row>
<b-card col class="w-50" header="Bar Chart" border-variant="primary" header-bg-variant="primary"
header-text-variant="white" align="center">
<apexchart width="100%" type="bar" :options="boptions" :series="bseries"></apexchart>
</b-card>
<b-card col class="w-50" header="Donut Chart" border-variant="primary" header-bg-variant="primary"
header-text-variant="white" align="center">
<apexchart width="100%" type="donut" :options="doptions" :series="dseries"></apexchart>
</b-card>
<b-card col class="w-100" header="Line Chart" border-variant="primary" header-bg-variant="primary"
header-text-variant="white" align="center">
<apexchart width="100%" type="line" :options="loptions" :series="lseries"></apexchart>
</b-card>
</b-row>
</b-container>
</div>
<!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
<script src="../uibuilder/vendor/vue/dist/vue.js">/* prod version with component compiler */</script>
<script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.min.js">/* remove 'min.' to use dev version */</script> <!-- -->
<!-- Loading from CDN, use uibuilder to install packages and change to vendor links -->
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-apexcharts"></script>
<script src="./uibuilder.iife.min.js">/* REQUIRED: remove 'min.' to use dev version */</script>
<script src="./index.js">/* OPTIONAL: Put your custom code in that */</script>
<!-- #endregion -->
</body></html>
/* globals Vue, uibuilder */
// @ts-nocheck
'use strict'
/** Reference the apexchart component (removes need for a build step) */
Vue.component('apexchart', VueApexCharts)
const ldata = [30, 40, 45, 50, 49, 60, 70, 91]
const laxis = [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998]
const app = new Vue({
el: '#app',
data() { return {
startMsg : 'Vue has started, waiting for messages',
imgProps : { width: 75, height: 75 },
//#region ---- Data for bar chart ----
boptions: {
chart: {
id: 'vuechart-bar-example'
},
xaxis: {
categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998],
},
},
bseries: [{
name: 'series-1',
data: [30, 40, 45, 50, 49, 60, 70, 91],
}],
//#endregion ---- ---- ----
//#region ---- Data for donut chart ----
doptions: {
chart: {
id: 'vuechart-donut-example'
},
},
dseries: [44, 55, 41, 17, 15],
//#endregion ---- ---- ----
//#region ---- Data for line chart ----
loptions: {
chart: {
id: 'vuechart-line-example'
},
xaxis: {
categories: laxis, // see updLchart for why we use an external var here
},
},
lseries: [{
name: 'series-1',
data: ldata,
}],
//#endregion ---- ---- ----
}}, // --- End of data --- //
computed: {}, // --- End of computed --- //
methods: {
// Update the bar chart from msg.payload
// Bar charts have complex data so have to replace the whole data series
// in order for VueJS to recognise it has changed.
updBchart(msg) {
// Replace random element with new value
const bdata = this.bseries[0].data
bdata[Math.floor(Math.random()*this.boptions.xaxis.categories.length)] = Math.floor(msg.payload)
// Yes, we have to replace the whole data series - the outer-most property MUST be updated directly
this.bseries = [ { data: bdata } ]
// If adding to this chart, you need to replace this.boptions not this.boptions.xaxis
},
// Update the Donut chart from msg.payload
// Pie/Donut charts have simple data so only need simple updates
updDchart(msg) {
// Add new element
this.dseries.push(msg.payload)
// Lose the first element
this.dseries.shift()
},
// Update the line chart from msg.payload
// Line charts have complex data so have to replace the whole data series
// in order for VueJS to recognise it has changed.
updLchart(msg) {
// Add new point with new value
// const ldata = this.lseries[0].data
// ldata.push( Math.floor(msg.payload) )
ldata.push( Math.floor(msg.payload) )
// Yes, we have to replace the whole data series - the outer-most property MUST be updated directly
this.lseries = [ { data: ldata } ]
// Add matching x-axis entry
// WARNING: Don't try to grab the xaxis direct from the loptions - for some reason, this results
// in the data being lost. A bug in Vue or ApexCharts, not sure which.
laxis.push( ++laxis[laxis.length-1] )
this.loptions = { xaxis: { categories: laxis } }
// Slice an entry off the front for next time around if >100 entries to keep things reasonable
if (laxis.length >= 100 ) {
ldata.shift()
laxis.shift()
}
},
}, // --- End of methods --- //
mounted: function(){
const app = this // Reference to `this` in case we need it for more complex functions
// If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
uibuilder.onChange('msg', (msg)=> {
if (typeof msg.payload === 'number') {
app.updBchart(msg)
app.updDchart(msg)
app.updLchart(msg)
}
})
}, // --- End of mounted hook --- //
}) // --- End of app1 --- //
// EOF
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)