Skip to content

Vue v3 Apache ECharts (no build)

Julian Knight edited this page Jul 15, 2022 · 1 revision

This is a simplistic example of using Apache ECharts with Node-RED, uibuilder, Vue v3 and the official vue-echarts support library. It also works with VueJS v2.

Note that is uses the IIFE version of the uibuilder front-end library from uibuilder v5.1.1+.

Sorry, no example of updating the data in this. Only getting the libraries to work. However, this is also an example of using the Quasar framework, also with no build step.

NOTE: You MUST set at least the height property of the div containing the chart. If you do not, the chart will not display. You may also need to set the background colour.

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Minimal modern client example - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - Minimal modern client example">
    <link rel="icon" href="./images/node-blue.ico">

    <link href="https://cdn.jsdelivr.net/npm/quasar@latest/dist/quasar.prod.css" rel="stylesheet" type="text/css">
    <!-- <link type="text/css" rel="stylesheet" href="./index.css" media="all"> -->

</head><body class="uib">
    
    <h2>uibuilder Minimal Modern Client Example using IIFE library</h2>

    <div id="q-app" class="q-ma-md">
        <q-btn id="sendToNR" color="primary" @click="doEvent" data-from="from client" data-something="else">
            Send <br><code>data-*</code> attribs to Node-RED
        </q-btn>
        <q-btn id="sendToNR2" color="primary" @click="doSend" data-from="from client using eventSend" data-something="else">
            Send to Node-RED
        </q-btn>

        <div class="q-ma-md">
            <q-btn label="Notify" color="primary" @click="fnNotify"></q-btn>
        </div>

        <div class="q-ma-md">
            Running Quasar v{{ $q.version }}
        </div>

        <syntax-highlight auto="msg">Latest msg from Node-RED:</syntax-highlight>

        <div class="title-row">
            <label for="chart1">Set the chart title here:</label>
            <input id="chart1" type="text" v-model="title">
        </div>
        <div id="main" style="width:80%;height:25rem;background-color:whitesmoke;">
            <v-chart autoresize :option="option"></v-chart>
        </div>
        
    </div>

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <script src="https://cdn.jsdelivr.net/npm/vue@latest/dist/vue.global.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@latest/dist/quasar.umd.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@latest/dist/lang/en-GB.umd.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/quasar@latest/dist/icon-set/svg-material-icons.umd.prod.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/echarts@latest"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue-echarts@latest"></script>
    <script src="../uibuilder/uibuilder.iife.min.js">/* REQUIRED. NB: socket.io not needed */</script>
    <script src="./index.js">/* OPTIONAL: Put your custom code here */</script>
    <!-- #endregion -->

</body></html>

index.js

// @ts-nocheck

/** Minimalist code for uibuilder and Node-RED */
'use strict'

// return formatted HTML version of JSON object
import('../uibuilder/vendor/@totallyinformation/web-components/components/syntax-highlight.js')
const { useQuasar } = Quasar

// Send a message back to Node-RED
window.fnSendToNR = function fnSendToNR(payload) {
    uibuilder.send({
        'topic': 'msg-from-uibuilder-front-end',
        'payload': payload,
    })
}

// 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 = Vue.createApp({
    // Define Vue reactive variables
    data() { return {
            count: 0,

            option: {
                textStyle: {
                    fontFamily: 'Inter, "Helvetica Neue", Arial, sans-serif',
                },
                title: {
                    text: 'Traffic Sources',
                    left: 'center',
                },
                tooltip: {
                    trigger: 'item',
                    formatter: '{a} <br/>{b} : {c} ({d}%)',
                },
                legend: {
                    orient: 'vertical',
                    left: 'left',
                    data: [
                        'Direct',
                        'Email',
                        'Ad Networks',
                        'Video Ads',
                        'Search Engines',
                    ],
                },
                series: [
                    {
                        name: 'Traffic Sources',
                        type: 'pie',
                        radius: '55%',
                        center: ['50%', '60%'],
                        data: [
                            { value: 335, name: 'Direct' },
                            { value: 310, name: 'Email' },
                            { value: 234, name: 'Ad Networks' },
                            { value: 135, name: 'Video Ads' },
                            { value: 1548, name: 'Search Engines' },
                        ],
                        emphasis: {
                            itemStyle: {
                                shadowBlur: 10,
                                shadowOffsetX: 0,
                                shadowColor: 'rgba(0, 0, 0, 0.5)',
                            },
                        },
                    },
                ],
            },

            title: ''
    } },

    watch: {
        title(newValue) {
            if (newValue === '') {
                    this.option.title.text = 'Entire title'
            } else {
                    this.option.title.text = newValue
            }
        }
    },

    // Reactive functions
    methods: {
        increment() {
            this.count++
        },

        fnNotify() {
            this.$q.notify('Running on Quasar v' + this.$q.version)
        },

        // Use the uib helper function to send something to NR
        doEvent(event) { uibuilder.eventSend(event) },

        // Use the normal send fn to send something to NR
        // NOTE: You cannot pass the domevent, domevent.currentTarget, or domevent.path[0] direct, you have to access specific properties
        doSend(domevent) {
            console.log('>> doSend >>', domevent)

            uibuilder.send({
                'topic': 'msg-from-uibuilder-front-end',
                'payload': payload,
            })
        },
    },

    // Lifecycle hooks
    created() {
        // No need to pre-define $q when working with the options API
        console.log('$q', this.$q)
        console.log(`>> Is Dark Active: ${this.$q.dark.isActive}, Dark Mode: ${this.$q.dark.mode}`)
    },

    mounted() {
        console.log(`The initial count is ${this.count}.`)

        // If msg changes - msg is updated when a standard msg is received from Node-RED
        uibuilder.onChange('msg', function (msg) {
            console.log('>> msg recvd >>', msg)
            Quasar.Notify.create('New message from Node-RED!')
            app.lastMsg = msg
        })
    },
})

app.component('v-chart', VueECharts)
app.use(Quasar, {
    config: {
        dark: 'auto' // or Boolean true/false
    }
})
Quasar.lang.set(Quasar.lang.enGB)
Quasar.iconSet.set(Quasar.iconSet.svgMaterialIcons)
app.mount('#q-app')
Clone this wiki locally