0

I have an Object allevents asynchronously updated with keys and values. Whenever allevents is modified I would like to trigger a recalculation function.

To do so, I use the following Vue structure (in a component, all irrelevant elements removed):

export default {
        data: function () {
            return {
                //
                allevents: {},
                events: []
            }
        },
        methods: {
            //
            mqttMessage(topic, message) {
                const cal = topic.split('/').pop()
                this.allevents[cal] = JSON.parse(message).filter(x => true)
                // following the update above, I was expecting that since 
                // allevents is watched below, the function would trigger
                // as a workaround, I added this line below which fixes the issue
                // but I still would like to understand the lack of trigger
                this.computeEvents()
            },
            // the function ssupposed to be triggred after a chnage, below
            computeEvents() {
                this.events = []
                Object.values(this.allevents).forEach(cal => cal.forEach(e => this.events.push(e)))
            }
        },
        watch: {
            // whenever allevents change, run computeEvents() <-- this does not happen
            allevents() { 
                console.log("events compute triggered")
                this.computeEvents() 
            }
        },
        mounted() {
            //
        }
}

Even though allevents is modified and is watched, computeEvents() is not started. Why?

WoJ
  • 27,165
  • 48
  • 180
  • 345

1 Answers1

2

It appears you are victim of a Reactivity/Change Detection Caveat.

Instead of:

this.allevents[cal] = JSON.parse(message).filter(x => true)

Try:

Vue.set(this.allevents, cal, JSON.parse(message).filter(x => true))

Or:

this.$set(this.allevents, cal, JSON.parse(message).filter(x => true))

Relevant excerpt from the docs:

Change Detection Caveats

Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive. For example:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` is now reactive
vm.b = 2
// `vm.b` is NOT reactive

Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it's possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:

Vue.set(vm.someObject, 'b', 2)

You can also use the vm.$set instance method, which is an alias to the global Vue.set:

this.$set(this.someObject, 'b', 2)
tony19
  • 125,647
  • 18
  • 229
  • 307
acdcjunior
  • 132,397
  • 37
  • 331
  • 304