vue.jsavatar

Vue.js prop sync modifier not updating parent component


I have a property that I need to pass to a child component, but the child component needs to be able to modify the value that was passed. It seems like the .sync modifier is built for this, but I can't seem to get it to work. Here is my code (simplified for this question):

Profile.vue

<template>
  <div>
    <Avatar :editing.sync="editing"></Avatar>
    <a href="" @click.prevent="changeAvatar">click to change</a>
    ...
  </div>
</template>

<script>
import Avatar from './profile/Avatar'

export default {
  components: { Avatar },
  data() {
    return {
      ...,
      editing: false,
    }
  },
  methods: {
    editAvatar() {
      this.editing = true;
    }
  }
}
</script>

Avatar.vue

<template>
  <div>
    <template v-if="!editing">
      <img class="avatar-preview" :src="avatar">
    </template>
    <template v-else>
      <label v-for="image in avatars">
        <img class="avatar-thumb" :src="image">
        <input type="radio" name="avatar" :checked="avatar === image">
      </label>
      <button class="btn btn-primary">Save</button>
    </template>
  </div>
</template>

<script>
export default {
  props: ['editing'],
  data() {
    return {
      avatar: '../../images/three.jpg',
      avatars: [
        '../../images/avatars/one.jpg',
        '../../images/avatars/two.jpg',
        '../../images/avatars/three.jpg',
        ...
      ]
    }
  },
  methods: {
    save() {
      axios.put(`/api/user/${ user.id }/avatar`, { avatar: this.avatar }
        .then(() => { console.log('avatar saved successfully'); })
        .catch(() => { console.log('error saving avatar'); })
        .then(() => { this.editing = false; }); //  ← this triggers a Vue warning
    }
  }
}
</script>

Solution

  • You are correct - the .sync modifier is built for cases just like this. However, you are not quite using it correctly. Rather than directly modifying the prop that was passed, you instead need to emit an event, and allow the parent component to make the change.

    You can resolve this issue by changing the save() method in Avatar.vue like this:

    ...
    save() {
      axios.put(`/api/user/${ user.id }/avatar`, { avatar: this.avatar }
        .then(() => { console.log('avatar saved successfully'); })
        .catch(() => { console.log('error saving avatar'); })
        .then(() => { this.$emit('update:editing', false); });
      }
    }
    ...