0

I already read some similar topics about my problem, but still I can’t figure out, what can be the problem is. Long story short, I have an array of objects, and these objects also have an array, called “messages”. The parent object has some additional data, and my problem is caused by the child array, the messages.

The problem is: The messages array is filled up by the server. After it, I successfully render a list of it’s content, show it on the web page, so far so good. But as soon as a new data arrives from the server, the rendered list is not updating itself. I checked the array’s content via the Vue dev plugin, and the new message was there, but the rendered list still remained in it’s original state.

The twist in the story is, that I created a simple test array, which is very similar to the original one, and also rendered on the page. When a new message arrives, I push it to both of them, and to my surprise, each one is updating itself perfectly. For the sake of curiosity, I commented out the test array rendering, and believe it or not, the original array does not update itself anymore on the page. After I remove the commented test array render, both of them works again.

The vue component:

/**
 * Root
 */

import Vue from 'vue'
import axios from 'axios'

import inputComponent from './chat/input.vue'

new Vue({
    el: '#chat-root',
    components: { inputComponent },
    data() {
        return {
            userID: document.getElementById('user').value,
            ready: false,
            roomID: "",
            roomInstances: [],
            test: [
                {
                    messages: []
                }
            ],
            pollTime: 5000
        }
    },
    created() {
        axios.get('chat/init')
            .then(resp => {
                this.roomID = resp.data.roomList[0]._id
                this.roomInstances = resp.data.roomList
                this.fetchMessagesFromServer()
                this.poll()
                this.heartBeat()
            }).catch(err => console.log(err))
    },
    computed: {
        getMessages() {
            return this.roomInstance.messages
        },
        roomInstance() {
            return this.roomInstances.filter(i => i.id = this.roomID)[0]
        }
    },
    methods: {
        setRoom(id) {
            if (this.roomID === id)
                return
            this.roomID = id
        },
        fetchMessagesFromServer() {
            axios.get(`chat/get/${this.roomID}`)
                .then(resp => {
                    this.roomInstance.messages = resp.data.messages
                    this.ready = true
                }).catch(err => console.log(err))
        },
        heartBeat() {
            setInterval(() => axios.get(`heartbeat/${this.userID}`), this.pollTime - 1000)
        },
        poll() {
            axios.create({ timeout: this.pollTime })
                .get(`poll/${this.userID}`).then(resp => {
                    if (resp.data.data) {
                        this.roomInstance.messages.push(resp.data.data.message)
                        this.test[0].messages.push(resp.data.data.message)
                    }
                    this.poll()
                }).catch(err => console.log(err))
        }
    }
})

Template:

extends base

block scripts
    script(src="/dist/chat.bundle.js")

block content

    #chat-root(style="margin-top: 50px" v-cloak)

        // Hack! Supposed to be an axios request?
        input(v-if="false" id="user" value=`${user._id}`)

        input-component(:room="roomID")

        p {{ roomID || "Whoo hooo hoooo!" }}

        span(v-for="room in roomInstances" @click="setRoom(room._id)") {{ room.name }} | 

        div(v-if="ready")
            div(v-for="message in getMessages")
                p {{ message }}
            hr
            div(v-for="fe in test[0].messages")
                p {{ fe }}
5
  • 1
    please include your code in the post itself instead of just linking to it Commented Feb 19, 2018 at 17:09
  • Thanks, fixed. :) Commented Feb 19, 2018 at 17:21
  • as @thanksd said you should make roomInstance.message reactive and always add key to v-for Commented Feb 19, 2018 at 17:43
  • @TomaszKostuch binding a key with v-for is really only necessary when using v-for with a component tag. Commented Feb 19, 2018 at 17:45
  • You are right :) Thank you, I forgot about it :) Commented Feb 21, 2018 at 5:32

1 Answer 1

2

For one, you are not finding the roomInstance correctly in the function for that computed property. You should just use the find method and return i.id === this.roomID in the callback instead of i.id = this.roomID:

roomInstance() {
  return this.roomInstances.find(i => i.id === this.roomID);
}

The other issue might be when you are setting roomInstance.messages in the axios.get().then callback. If roomInstance.messages is undefined at that point, you'll need to use the Vue.set method (aliased on the Vue instance via this.$set) in order to make that property reactive:

this.$set(this.roomInstance, 'messages', resp.data.messages)

Here's the Vue documentation on Change Detection Caveats.

Sign up to request clarification or add additional context in comments.

2 Comments

I fixed the code based on your suggestion, thank you! However, I face to a new issue, which says: TypeError: Cannot use 'in' operator to search for 'messages' in undefined.
I solved the "undefnied" problem, it was my fault. this.$set saves the day.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.