gb_spectrum
gb_spectrum

Reputation: 2301

vue.js v-for list not updating

I have this list:

<ul id="tab">
    <li v-for="list in names">
        {{ list.personName }}
    </li>
</ul>

And then I have this vue object:

var vm = new Vue ({
    el: '#tab',
        data: {
            names: //an object array coming from the server
        }
    });

So the 'names' data is updated and given information from the server. However, when the names are updated/change, it is not reflected on the client side list. The items that appear in the list only reflect the items that were there when the page initially loaded.

In the vue.js developer tools in Google Chrome, I can always see the updated 'names' data, but it just doesn't reflect on the DOM itself.

EDIT1: What's in the 'name's:

names: Array[2]
    0: Object
    _id: "580aeafd017fbfb81a3a794d"
    personName: "hi"

    1: Object
   _id: "580c4455ccc9e39c21f02434"
   personName: "test"

EDIT2

So I'm using node.js, and transferring the data live from node to the client via socket.io like this:

socket.on('userDocument', function(userDocument) {
    var vm= new Vue ({
        el: '#tab',
        data: {
            names: //an object array coming from the server
        }
    });
});

Upvotes: 33

Views: 64981

Answers (5)

vizmi
vizmi

Reputation: 2032

Array change detection is a bit tricky in Vue. Most of the in place array methods are working as expected (i.e. doing a splice in your $data.names array would work), but assigining values directly (i.e. $data.names[0] = 'Joe') would not update the reactively rendered components. Depending on how you process the server side results you might need to think about these options described in the in vue documentation: Array Change Detection.

Some ideas to explore:

  • using the v-bind:key="some_id" to have better
  • using the push to add new elements
  • using Vue.set(example1.items, indexOfItem, newValue) (also mentioned by Artokun)

Upvotes: 38

Khaled Developer
Khaled Developer

Reputation: 47

i have a solve. Use a Component without data. instead of data , use props for send data to your component :) it's will work.

Your List:

<ul id="tab">
    <my-li v-for="name in names" :my-name="name"></my-li>
</ul>

or Your List:

<ul id="tab">
    <my-li v-for="name in names">{{name}}</my-li>
</ul>

Your Script:

let Mycomponent = Vue.component("my-li",{
   props:['my-name'],
   template:"<li>{{myName}}<slot></slot></li>"
});
var vw = new Vue({
  el:"#tab",
  data:{
    names:['ali','reza','matin','mehdi','ahora','me']  
  },
  methods:{
          changeList:function(myList,index,newVal){
              let backupList = this.names;
              backupList[index] = newVal;
              this.names = []
              this.names = backupList;
            }
  },
  component:[Mycomponent]
  })

you can change Value of list from this method:

vw.changeList(vw.names,1,"hello")

if you need set a list to Names Use this

vw.names = [];
vw.names = YourList;

This Is Easy man :)

result: jsfiddle.net/khaled_developer/5n316jpo

Upvotes: 1

ADM-IT
ADM-IT

Reputation: 4184

What I found is even if you pop and then push the element back to the list v-for will not update the list.

For example the code this.items.pop(); will update the list but the this.items.push(this.items.pop()); won't. Have no idea why.

As a solution I did the following trick (very ugly, sorry) and it works:

<Column v-if="col in columns" ....

methods: {
    refreshColumns() {
        this.columns_temp = [...this.columns];
        this.columns = [];
        this.$nextTick(() => {
            this.columns = [...this.columns_temp];
        });
    },
}

I will let you know if I find a proper solution. But at the moment I have no time to do that.

Upvotes: 2

Mani Jagadeesan
Mani Jagadeesan

Reputation: 24265

I am not an expert on sockets, but that is not the recommended way of handling a socket connection.

A socket sets up a persistent connection with server, and you may get data at any moment from server. Your socket.on("message", function(data) {...}) is the handler for these asynchronous data from server.

In your sample code, your message handler seems to create a new instance of Vue() every time the server sends some data. You will quickly end up with multiple versions of Vue() instances, potentially leading to crashing user's browser tab by hogging memory.

I do not use socket.io, but based on my understanding of web apps in general, here is how I would prefer to do it:

// Initialize your Vue app first:
new Vue ({
    el: '#tab',
    template: `
        <ul id="tab">
            <li v-for="list in names">
                {{ list.personName }}
            </li>
        </ul>
    `
    data: {
        names: [] // start with blank, and modify later
    },
    created: function() {
        // This 'created' hook is called when your Vue instance is setup
        // TODO: Initialize your socket connection here.
        // ...
        // Setup your socket listener
        mySocketInstance.on("message", response_data => {
            // Assuming your response_data contains 'user_names' array
            this.names = response_data.user_names;
            // Note: 'this' in the above line points to outer scope, that is why we need arrow functions
        })
    }
});

The above code might work for you. You still have to do a lot of testing and debugging. But this is how I would expect things to work.

Note:

  1. The code sample creates only one Vue() instance, and initializes socket listener in the created hook. So there is only one listener / handler for your socket messages.

  2. The socket message handler uses javascript arrow function which ensures that your outer scope this is same as inner scope this. Thus you will be able to update your names correctly.

Upvotes: 2

Artokun
Artokun

Reputation: 573

If it's getting passed in as an object from the server, make sure to use Vue.set(obj, key, value) when binding reactively to data().

http://vuejs.org/api/#Vue-set

Upvotes: 9

Related Questions