Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ronon's avatar
Level 9

Image src won't update

In a chat I create a message as an placeholder. Then I upload the image and receive the url. I set the empty attribute of the message object to the new url. But it won't update the image. If I manipulate any message attribute via console it gets updated and shows the image.

Only display the html part, if an url is available. I tried it with the transition 'option' as well, but with no success.

<div class="attachment" v-if="hasAttachment">
            <transition name="fade">
            <a :href="message.attachment_url" :key="'url'+message.message_id" data-fancybox data-caption="">
                <img :src="message.attachment_url" :key="'image'+message.message_id">
            </a>
            </transition>
        </div>`

The wrap in the a tag is because of fancybox.

After the file upload with axios I do this.message.attachment_url = response.data.url; but as mentioned it won't change the html attributes. What am I doing wroing?

If already found this post https://forum.vuejs.org/t/how-to-add-animation-for-image-src-change/4720/2 but the :key option didn't help

0 likes
16 replies
MaverickChan's avatar

if you are using chrome , that is chrome's cache bug.

you can add some sort of unique number at the end of your href

such as :

<img :src="your.vue.data + '?' + Math.Random()">

or , someone says , use src and srcset together

2 likes
ronon's avatar
Level 9

I don't think that it is a cache issue. It's also missing in the DOM.

Looks like:

<div class="attachment"><a data-fancybox="" data-caption=""><img></a></div>

but should look like: <div class="attachment"><a href="url/to/my/uploaded/image" data-fancybox="" data-caption=""><img src="url/to/my/uploaded/image"></a></div>

The Vue Inspector for chrome tells me that attachment_url is present in the message prop

Nash's avatar

Remove the this keyword from your HTML template.

<a :href="message.attachment_url">
    <img :src="message.attachment_url">
</a>

Edit: updated the example

ronon's avatar
Level 9

That was a dumb mistake, but didn't change the result.

Nash's avatar

@ronon Silly question, but did you update the img element as well? I updated my previous answer to reflect this.

ronon's avatar
Level 9

Yes, I updated the img element as well. Updated the code from my post as well.

Nash's avatar

What does your JS code look like? Is message part of the component data like this?

data () {
    return {
        message: {}
    }
}
ronon's avatar
Level 9

Here is the code of the component.

<template lang="html">

<div class="message">
    <div class="chat-message" :class="classSelf">
        <div class="message-author" v-if="displayAuthor === true">{{ message.user.firstname }}</div>
        <div class="attachment" v-if="hasAttachment">
            <transition name="fade">
            <a :href="message.attachment_url" :key="'url'+message.message_id" data-fancybox data-caption="">
                <img :src="message.attachment_url" :key="'image'+message.message_id">
            </a>
            </transition>
        </div>
        <div class="message-text" v-html="parseMessage" v-linkified v-if="hasMessage"></div>
        <div class="message-meta">
            <span class="time">{{ time }}</span>
        </div>
    </div>
</div>

</template>

<script>

export default{
    props: ['message', 'conversation'],
   
    computed: {
        displayAuthor: function () {
            if(this.conversation.private === true)
                return false;
            return this.message.user.id === CurrentUser.id;
        },
        time: function () {
            return moment.unix(this.message.created_at).format("HH:mm");
        },
        parseMessage: function () {
            const twemoji = require('twemoji');

            return twemoji.parse(this.message.message);
        },
        hasAttachment(){
            // File needs to be uploaded
            if(_.has(this.message, 'files')){
                return true;
            }
            // url available?
            return !_.isEmpty(this.message.attachment_url);
        },
        hasMessage(){
            return !_.isEmpty(this.message.message);
        }

    }
}

</script>

As base I used this https://jplhomer.org/2017/01/building-realtime-chat-app-laravel-5-4-vuejs/ tutorial.

The code to upload an file:

 // Upload file
        uploadFile(files){
            console.log('ChatMain upload file');
            var uploadData = new FormData();
            uploadData.append('type', 'chat_attachment');
            uploadData.append('file', files[0] );

             // If i do this, it works?!???
            //this.message.attachment_url = 'http://via.placeholder.com/150?text=loading';

            axios.post('/upload/image', uploadData, {
                onUploadProgress: progress => console.log(progress)
            }).then(response => {
                console.log("upload finished");
                console.log(response.data);

                if(response.data.error === false){

                    this.message.attachment_id = response.data.image_id;
                    // Wont update the image attribute
                    this.message.attachment_url = response.data.url;

                    this.sendMessage();
                } else {
                    alert(response.data.message);
                }

            });
        }
ronon's avatar
Level 9

I iterate over it in the parent component ChatLog. I normally have some additional div tags to display additional intel like dates etc. in the for-loop aswell, but they aren't neccesary for it.

<template lang="html">
<div class="chat-log chat-background" v-chat-scroll>
    <div v-for="(message, index) in messages">

        <chat-message :message="message" :conversation="conversation" :key="message.message_id"></chat-message>

    </div>

    <div class="empty" v-show="conversation.length === 0">
     Choose a conversation
    </div>

</div>
</template>

<script >
import Vue from 'vue'
import VueChatScroll from 'vue-chat-scroll'
Vue.use(VueChatScroll)

export default{
    props: ['messages', 'conversation'],
    methods: {
       
    }

}
</script>

In Chatcomposer I can write a message and choose a file. All ChatLog and ChatComposer are located in a component called ChatMain. In the ChatMain component the files are also uploaded and the message is created.

<template lang="html">
<div class="col s12 m12 l8 card-panel no-padding">

    <div class="chat-main">
        <chat-log :messages="messages" :conversation="conversation"></chat-log>
        <chat-composer v-on:addmessage="addMessage"></chat-composer>
    </div>

</div>
</template>

<script>
export default{
    props: ['conversation', 'messages', 'conversationId'],
    data(){
        return {
            message: {}
        }
    },
    methods: {
        // Add message to array, check if files need to get uploaded
        addMessage(obj){

            console.log('add message');
            console.log(obj);
            this.message = obj;
            this.message.state = 'Loading';

            // Push current message to last message list
            this.conversation.last_messages.push(this.message.message);
            // Update timestamp
            this.conversation.updated_at = _.now();

            this.messages.push(this.message);
            if(_.has(this.message, 'files')){

                this.uploadFile(this.message.files);
            } else {
                this.sendMessage();
            }

        },
        // emit sendmessage. chat.js listens for it
        sendMessage: function () {
            console.log("ChatMain: sent message");
            console.log(this.message);
            this.$emit('messagesent', this.message);
        },
        // Upload file
        uploadFile(files){
            console.log('ChatMain upload file');
            var uploadData = new FormData();
            uploadData.append('type', 'chat_attachment');
            uploadData.append('file', files[0] );

            axios.post('/upload/image', uploadData, {
                onUploadProgress: progress => console.log(progress)
            }).then(response => {
                console.log("upload finished");
                console.log(response.data);

                if(response.data.error === false){

                    this.message.attachment_id = response.data.image_id;
                    this.message.attachment_url = response.data.url;

                    this.sendMessage();
                } else {
                    alert(response.data.message);
                }

            });
        }
    }
}

this.$emit('messagesent', this.message); calls a function in app.js. It simply pushes the message to the server.

Nash's avatar

You should probably go through each component step by step. Check all props and verify that the message/messages and related data is passed around correctly and accessible in every component as needed. For example: apart from the image url, is the rest of the message content getting through to the ChatMessage component correctly? Does the axios response contain the image url at all? Is the messages prop in ChatMain updated with this new url data?

ronon's avatar
Level 9

Checked all multiple times. Url is also set by vue but it just won't get displayed.

Maybe a problem because it's delayed?

If I assign any image url before axios like

  // Upload file
    uploadFile(files){
        console.log('ChatMain upload file');
        var uploadData = new FormData();
        uploadData.append('type', 'chat_attachment');
        uploadData.append('file', files[0] );

         // If i do this, it works?!???
        this.message.attachment_url = 'http://via.placeholder.com/150?text=loading'; // <- Works

      /*  axios.post('/upload/image', uploadData, {
            onUploadProgress: progress => console.log(progress)
        }).then(response => {
            console.log("upload finished");
            console.log(response.data);

            if(response.data.error === false){

                this.message.attachment_id = response.data.image_id;
                // Wont update the image attribute
                this.message.attachment_url = response.data.url;  <- won't work

                this.sendMessage();
            } else {
                alert(response.data.message);
            }

        });*/
    }

Vue inspector also shows mit that the correct image url is available in the message object. If I manipulate any attribute from the element over the console, it is going to get displayed, but only if I make sth with the object

Nash's avatar

Are you certain that the code block inside the if(response.data.error === false) conditional is fired and that response.data.url really contains the image url? It would seem that the problem is somewhere within the axios part if it works when set before that?

ronon's avatar
Level 9

Yes it is fired also the url is set. Vue just won't update the html for any reasons :/

erikverbeek's avatar

I think it could be that the 'this' from

this.message.attachment_id = response.data.image_id;

points to the anonymus function you use within your .then() instead of it being your Vue instance. You can see if this is the case by logging your 'this' attribute to your console.

If this is the case you can fix this by binding Vue's 'this' to the function like so

}).then(response => {
 // Your code
}.bind(this)

That will overwrite where 'this' points to within that function.

Or you could assign your Vue instance to a variable like:

var vue = new Vue({

and then you should be able to use

vue.message.attachment_id = response.data.image_id;

Please or to participate in this conversation.