Search code examples
eventsscrollvue.jscomponents

Vuejs emit event when datas are rendered


In vuejs, I'm developing a conversation chat between two users. I have nested component like this

Chat
    ->ChatLog
        -> ChatMessage
    ->ChatComposer

The chat component has the structure of the chat (a panel in bootstrap) and also that's where I retrieve with Axios, when the page is loaded, all the messages from the database.

axios
.get('/conversation/'+this.conversation.id+'/message')
.then((response) => {
    this.messages = response.data;
    this.manageHeightChat();
    this.scrollToEnd();
});

And when the I get all the message, I dispatch them to the ChatLog component

<chat-message v-for="message in messages" v-bind:message="message" v-bind:user="user"></chat-message>

which dispatch each message to the ChatMessage component.

So when the callback method is called with all the messages, they are dispatched to the other components and then the two methods manageHeightChat and scrollToEnd are called.

scrollToEnd() {
    var container = this.$refs.chatlog;
    container.scrollTop = container.scrollHeight;
    console.log(container.scrollHeight);
}

The problem is that the last method (scrollToEnd) is called before the messages are rendered by the ChatLog and ChatMessage so the container.scrollHeight is not the good size.

To verify it I added something like :

setTimeout(function () {
    this.scrollToEnd();
}.bind(this), 1000);

And everything was working fine.

So how and where would you put some event to tell chat that the content is rendered and that I can call the scrollToEnd method ?


Solution

  • See nextTick. When you set the message data, you need to wait for Vue to update the DOM. That is what nextTick is for. Use it pretty much like you're using setTimeout

    axios
    .get('/conversation/'+this.conversation.id+'/message')
    .then((response) => {
        this.messages = response.data;
        this.manageHeightChat();
        this.$nextTick(this.scrollToEnd.bind(this));
    });