Deniss Muntjans
Deniss Muntjans

Reputation: 379

Update image src on the fly with VueJS

I'm a new VueJS user, currently struggling with updating image src on the fly. This is what I've got:

Template:

<div v-for="place in places">
    <img 
        v-bind:src="isPlacePrivate(place.data.place_is_private)" 
        v-on:click="setPlaceAsPrivate(place.data.place_is_private, place.data.place_id)"
    >
</div>
<script>
    export default {
        data: function () {
            return {
                places: null,
            }
        },

        mounted () {
            this.username = this.$route.params.username;

            axios.get('/api/' + this.username + '/places')
                .then(response => {
                    this.places = response.data.data;
                })
                .catch(error => {
                    // show error
                });
        },

        methods: {
            isPlacePrivate: function (value) {
                // If Place is private
                if (value == 1) {
                    var src = '/icons/padlock-color.png'
                } else {
                    var src = '/icons/padlock.png'
                }

                return src;
            },

            setPlaceAsPrivate: function (value, placeId) {
                let data = {
                    isPrivate: value
                };

                axios.put('/api/' + this.username + '/places/' + placeId + '/edit', data)
                    .then(response => {
                        let newValue = response.data.data.private;

                        this.isPlacePrivate(newValue);
                    })
                    .catch(error => {
                        // show error
                    });
            },
        },
    }
</script>

On a page load -> if a particular place is private it will show colored padlock icon or uncolored padlock if a place is public!

A user will be able to press on the padlock icon and change the value from public->private or private->public.

Everything is working fine but the padlock image is not updating on the fly when a user is clicking on it, I need to refresh a page to see changes! How to make it work?

Upvotes: 1

Views: 1855

Answers (2)

TJ Weems
TJ Weems

Reputation: 1114

I would suggest using a computed property so that it is reactive

Also according to your updates you are looping through an array of places so when you get your response from your axios call instead of just updating the icon I would try replacing the object in the array so I created the method called updatePlace() and I pass in the response object.

And change your places in the v-for to a computed property as well so that it is also reactive

Template:

<div v-for="place in placesArray" :key="index" v-if="places">
  <img 
    v-bind:src="imgSrc" 
    v-on:click="setPlaceAsPrivate(place.data.place_is_private, place.data.place_id)"
    v-if="imgSrc"
>
</div>

Script:

<script>
export default {
 data() {
    return {
       src: '',
       places: null 
    }     
  },
  computed: {
     imgSrc() {
        return this.src
     },
     placesArray() {
       return this.places
     }
  },
Methods: {
    isPlacePrivate: function (value) {
    // If Place is private
    if (value == 1) {
        this.src = '/icons/padlock-color.png'
    } else {
        this.src = '/icons/padlock.png'
    }
},
setPlaceAsPrivate: function (value, placeId) {
    let data = {
        isPrivate: value
    };
    axios.put('/api/' + this.username + '/places/' + placeId + '/edit', data)
        .then(response => {
            console.log(response);
            let newValue = response.data.data;
            this.updatePlace(newValue);
        })
        .catch(error => {
            console.log(error);
        });
    },
  },
  updatePlace(newPlace) {
    const index = this.places.findIndex(place => place.id === newPlace.id)
    this.places.splice(index, 1, place)
  },
  created() {
     this.username = this.$route.params.username;

        axios.get('/api/' + this.username + '/places')
            .then(response => {
                this.places = response.data.data;
            })
            .catch(error => {
                // show error
        });
   }
}
</script>

Also make sure to move your mounted method to a created() method so that it is called before anything else is trying to render.

Upvotes: 1

Rubens Barbosa
Rubens Barbosa

Reputation: 201

Apparently the problem is that you are calling the function and printing its return on the <img v-bind:src>, the isPlacePrivate function returns a value, so when you use this function within the setPlaceAsPrivate it returns the value only in scope of setPlaceAsPrivate. The isPlacePrivate function does not modify any data value of the component, so the image always remains the same. You just need to set a data and manipulate its value in the isPlacePrivate function.

Template

  <img 
        v-bind:src="bindSrc" 
        v-on:click="setPlaceAsPrivate(place.data.place_is_private, place.data.place_id)"
    >

Script

<script>
 export default {
     data() {
        return {
           bindSrc: '/icons/padlock-color.png',  // default img src value
           ... // your other values
        }     
    },
    Methods: {
        isPlacePrivate: function (value) {
        // If Place is private
        if (value == 1) {
            this.bindSrc = '/icons/padlock-color.png'
        } else {
            this.bindSrc = '/icons/padlock.png'
        }
    },
    setPlaceAsPrivate: function (value, placeId) {
        let data = {
            isPrivate: value
        };
        axios.put('/api/' + this.username + '/places/' + placeId + '/edit', data)
            .then(response => {
                console.log(response);
                let newValue = response.data.data.private;
                this.isPlacePrivate(newValue);
            })
            .catch(error => {
                console.log(error);
            });
        },
      }
    }
</script>

Upvotes: 1

Related Questions