Search code examples
vue.jsgoogle-visualizationvuex

Vue computed property functions are never called


I trying to do my own Google Science Journal, but web version with MQTT protocol, but my code have so many bugs.

Some clarification about my code:

  • google.charts.load is calling in the parent component, then callback make a call for the child component
  • time props is an interval, change between true and false every second for collect data.
  • I need to watch changes of a data with multiple dependencies.

How my code works?

  1. When a new instance is created, appear an empty google chart
  2. The chart start to show data every second.
  3. The tittle for the chart change when a new tab is selected and the chart data is erased.

This code has a problem, is the order in which the functions are called (generate some problems with google chart), but I fixed it, moving functions from computed to methods and calling in order.

I just want to know why this computed functions don't work, I mean, they aren't never call.

<template>
    <v-card>
        <v-tabs
        v-model="active_tab"
        background-color="red lighten-2"
        dark
        show-arrows
        >
            <v-tab
            v-for="(n) in tabs"
            :key="n.id"
            >
                {{n.text}}
                <v-icon>{{n.icon}}</v-icon>
            </v-tab>
        </v-tabs>
        <div ref="char_div"></div>
    </v-card>
</template>

<script>
import debounce from 'debounce'

export default {
    name: 'charts',
    props:{
        time: {
            type: Boolean,
            default: false
        }
    },
    data(){
        return {
            chart: null,
            options: null,
            value: 0,
            previus: null,
            data: null,
            tabs: [
                {id: 0, text: "Aceleración lineal", unit: "(m/s&sup2;)", icon: ""},
                {id: 1, text: "Aceleración en x", unit: "(m/s&sup2;)", icon: ""},
                {id: 2, text: "Aceleración en y", unit: "(m/s&sup2;)", icon: ""},
                {id: 3, text: "Aceleración en z", unit: "(m/s&sup2;)", icon: ""},
                {id: 4, text: "Velocidad lineal", unit: "(m/s)", icon: ""},
                {id: 5, text: "Luz ambiental", unit: "(lux)", icon: ""},
                {id: 6, text: "Intensidad sonora", unit: "(dB)", icon: ""},
                {id: 7, text: "Tono", unit: "(Hz)", icon: ""},
                {id: 8, text: "Barómetro", unit: "(hPa)", icon: ""},
                {id: 9, text: "Brujula", unit: "grados", icon: ""},
                {id: 10, text: "Magnetómetro", unit: "&micro;T", icon: ""},
                {id: 11, text: "Humedad", unit: "%", icon: ""},
                {id: 12, text: "Temperatura ambiente", unit: "&deg;C", icon: ""}
            ],
            active_tab: 0
        }
    },
    computed: {
        newData: function(){
            switch (this.active_tab) {
                case 0:
                    this.value = Math.sqrt(this.$state.lin_acel.x^2 + this.$state.lin_acel.y^2 + this.$state.lin_acel.z^2)
                    break
                case 1:
                    this.value = this.$state.lin_acel.x
                    break
                case 2:
                    this.value = this.$state.lin_acel.y
                    break
                case 3:
                    this.value = this.$state.lin_acel.z
                    break
                case 4:
                    if (this.previus != null){
                        //var cons = Math.sqrt(this.$state.lin_acel.x^2 + this.$state.lin_acel.y^2 + this.$state.lin_acel.z^2)
                        var ax = this.$state.lin_acel.x,
                            ay = this.$state.lin_acel.y,
                            az = this.$state.lin_acel.z

                        var nuevo = Date.now()

                        var vx = ax * ((nuevo - this.previus)/1000),
                            vy = ay * ((nuevo - this.previus)/1000),
                            vz = az * ((nuevo - this.previus)/1000)

                        //this.value += (cons)*((nuevo - this.previus)/1000)
                        this.value += Math.sqrt(vx^2 + vy^2 + vz^2)
                        this.previus = nuevo
                    }else{
                        this.value = Math.sqrt(this.$state.lin_acel.x^2 + this.$state.lin_acel.y^2 + this.$state.lin_acel.z^2)
                        this.previus = Date.now()
                    }
                case 5:
                    this.value = this.$state.lux
                    break
                case 6:
                    this.value = this.$state.noise
                    break
                case 7:
                    this.value = this.$state.noise
                    break
                case 8:
                    this.value = this.$state.presion
                    break
                case 9:
                    var vector = Math.sqrt(this.$state.magneto.x^2 + this.$state.magneto.y^2 + this.$state.magneto.z^2)
                    break
                case 10:
                    this.value = Math.sqrt(this.$state.magneto.x^2 + this.$state.magneto.y^2 + this.$state.magneto.z^2)
                    break
                default:
                    this.value = 0
                    break
            }
        },
        newOptions(){
            console.log("new options")
            this.options = {
                tittle: this.tabs[this.active_tab].text,
                crosshair: {orientation: 'vertical', trigger: 'both'},
                legend: 'none',
                hAxis: {
                    format:'mm:ss'
                    }
            }
        },
        drawchart(){
            console.log("chart is drawing")
            this.chart.draw(this.data, google.charts.Line.convertOptions(this.options));
        },
    },
    watch: {
        time: {
            immediate: false,
            handler(){
                this.addData()
            }
        },
        active_tab: {
            inmediate: false,
            handler(){
                this.updatetable()
            }
        }
    },
    methods: {
        addData(){
            this.data.addRows([[new Date(Date.now()),0]])
        },
        updatetable(){
            this.data = null
            this.data = new google.visualization.DataTable(
                {cols:
                    [{label: 'tiempo', id: 'x', type: 'date'},
                    {label: String(this.tabs[this.active_tab].text), id: 'y', type: 'number'}]})
        }
    },
    mounted() {
        this.chart = new google.charts.Line(this.$refs.char_div)
        this.newOptions
        this.updatetable()
        this.drawchart
    }
}
</script>

<style>

</style>

Solution

  • As Phil says, "Computed property functions are not meant to alter any data properties and are supposed to return a value". Is because computed watchs changes only in return, for example:

    //Computed watch this
    return this.a + this.b
    //{...}
    //Not this
    this.c = this.a + this.b
    

    I found here and in Vue forums ways to improve this code: If you need to watch a data with multiple dependencies, you can do the next:

    computed: {
        multi(){
            return [this.a, this.b].join()
        }
    },
    watch: {
        multi: {
            handler(): {
                this.c = this.a + this.b
            }
        }
    }
    

    I thing the best I can find solution was:

    created(){
        this.$watch(
            (this) => (vm.x, vm.y, vm.z, Date.now()),
            function () {
                // Executes if `x`, `y`, or `z` have changed.
            }
        )
    }
    

    This last also return the function unwatch(), it stop the watcher property, if I do this.watcher = this.$watch(/*data and callback here*/), then I can do this.watcher() stop watcher and make new one, perfect for improve performance of this code.