Vue: How to perform reactive object change detection in v-for?
Asked Answered
H

3

9

I read this documentation but cannot use the proposed solution.

I have a v-for loop over objects. These objects are changed dynamically over time and I need that change to show reactively in the v-for loop.

<b-row lg="12" v-for="data in objects" :key="data.id">
    <div v-if="data.loading">
      loading...
      {{data.loading}}
    </div>
    <div v-else>
      loaded, done
      {{data.loading}}
    </div>
</b-row>

In my methods, I have a for loop that downloads data for each object and changes the object value like this:

for(var i = 0; i<response.ids.length; i++){
    var newId = response.ids[i].id
    this.objects.newId = {"loading":true, "id": newId}

    downloadSomething(newId).then(res => {
        this.objects.newId = res[0]         //<-- this change needs to be shown reactively.
    })
}

According to Vue documentation, Object changes are not reactive:

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

vm.b = 2
// `vm.b` is NOT reactive

Vue propses some workaround like this:

Vue.set(vm.userProfile, 'age', 27)

UPDATE

But for my case, this just creates a new parameter in the object with the same ID and creates a duplicate key warning and other problems.

I also tried Vue.delete just before Vue.set but it is not actually deleting.

Is there a way to not replace the key/value pair but add more/change parameters to the first child of the root with the ID of newID

Thanks!

Haukom answered 30/11, 2018 at 11:56 Comment(0)
E
19

Solution: Replace this.objects.newId = res[0] with this.$set(this.objects, 'newId', res[0]) and it should work.

Explanation: this.$set is just an alias to Vue.set, available within any Vue instance. Bear in mind that vm (stands for view model) in the example is the same as this and equals to Vue component instance. Also due to ES5 JS restrictions, you have to set objects' properties explicitly via Vue.set/this.$set to manually trigger re-render. This problem will be resolved when Vue 3.0 is released.

Hope this helps, if you need any clarifications - feel free to ask.

Enchain answered 30/11, 2018 at 12:53 Comment(2)
I failed to mention explicitly that my 'objects' is an Object not Array. Vue.set/vm.$set creates a new parameter in the 'objects' with the same ID and doesn't replace the existing. This creates a duplicate key warning and other problems. I also tried Vue.delete just before Vue.set but Vue.delete is not working.Haukom
Figured it out. I was setting the initial key value for the child object without Vue.set and then expected it to change with Vue.set. As it turns out initial value (loading value) needs to also be set using Vue.set. I also helps with readability.Haukom
P
3

you need Vue.set() when you want to define a new property to an existing object (not directly to the data), and this function will assign the new property to the built-in watcher, and it will become reactive as well.

its all being explained in the docs: https://v2.vuejs.org/v2/guide/reactivity.html

in your case, it seems to be all you need to make it work. in my example:https://jsfiddle.net/efrat19/eywraw8t/484131/ an async function fetches the data and define a new property to contain te response.

new Vue({
  el: "#app",
  data: {
    objects:{
    property1:12
    }
  },
  methods: {
    fetchDataAndAddProperty(response){
        fetch('https://free.currencyconverterapi.com/api/v6/countries').then(res =>
      res.json()).then(data => Vue.set(this.objects,'property2',data))
    }
  }
})

and the template:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>

<div id="app">
<div lg="12" v-for="(val,key,index) in objects" :key="index">
    {{key}}:{{val}}
</div>
<button @click="fetchDataAndAddProperty()">fetch</button>
</div>

as you can see in the fiddle, the new property becomes reactive and being displayed as well.

Polynesian answered 30/11, 2018 at 12:42 Comment(0)
D
3

Try that:

downloadSomething(newId).then(res => {
    this.$set(this.objects, 'newId', res[0])
})
Demetri answered 30/11, 2018 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.