VueJS select component in single file

Viewed 109

I'm despaired. I absolutely have no clue anymore how to outsource html "select"-tag to a single Vue component file. I tried so much different ways but unfortunately I've never got it to work yet.

User component:

<custom-select :id="'continent'" :selected="user.continent" :options="continents" @change.native="changedContinent" class="some css classes" />

export default {
    props: ['user'],
    components: {
        CustomSelect,
    },
    data() {
        return {
            continents: [
                { value: 'africa', text: 'Africa' },
                { value: 'america-north', text: 'America (North)' },
                { value: 'america-south', text: 'America (South)' },
                { value: 'asia', text: 'Asia' },
                { value: 'australia', text: 'Australia' },
                { value: 'europe', text: 'Europe' }
            ]
        };
    },
    methods: {
        updateUser() {
            axios.post('/updateUser', this.user).then(() => {});
        },
        changedContinent() {
            // Do something with the new selected continent like changing background relative to the selected continent
        }
    }
};

CustomSelect:

<template>
    <select v-model="selected" class="some css classes">
        <option v-for="option in options" :key="option.value" :value="option.value">{{ option.text }}</option>
    </select>
</template>

<script>
export default { props: ['id', 'selected', 'label', 'options'] };
</script>

Whenever I change the selected value it should update user.continent value but instead, it throws the following error:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "selected"

But I do not know how to handle this. Could anyone help me please?
Thank you in advance!

1 Answers

As the error message says, 'to avoid mutating a prop directly, use a data or computed property ...'

I modified your code to create the components in my test environment (Vue 2 using Vue CLI), and bound the CustomSelect to a new data property 'selectedContinent' initialized from the 'selected' prop. I am also updating the parent component (User component in your case) via a custom event. Here are my components. See the Vue Custom Events documentation

My CustomSelect.vue component:

<template>
  <div class="custom-select">
    <select v-model="selectedContinent" class="" v-on:change="changeContinent">
      <option v-for="option in options" :key="option.value" :value="option.value">{{ option.text }}</option>
    </select>
  </div>
</template>

<script>
  export default {
    props: ['id', 'selected', 'label', 'options'],
    data() {
      return {
        selectedContinent: this.selected
      }
    },
    methods: {
      changeContinent() {
        //console.log('changeContinent: ' + this.selectedContinent);
        this.$emit('change-continent-event', this.selectedContinent);
      }
    }
  }
</script>

My Parent.vue component (your User):

<template>
  <div class="parent">
    <custom-select :id="'continent'" :selected="user.continent" :options="continents"
      v-on:change-continent-event="changedContinent" />
  </div>
</template>

<script>
  import CustomSelect from './CustomSelect.vue'

  export default {
    props: ['user'],
    components: {
      CustomSelect,
    },
    data() {
      return {
        continents: [
          { value: 'africa', text: 'Africa' },
          { value: 'america-north', text: 'America (North)' },
          { value: 'america-south', text: 'America (South)' },
          { value: 'asia', text: 'Asia' },
          { value: 'australia', text: 'Australia' },
          { value: 'europe', text: 'Europe' }
        ]
      };
    },
    methods: {
      updateUser() {
        //axios.post('/updateUser', this.user).then(() => { });
      },
      changedContinent(newContinent) {
        // Do something with the new selected continent like changing background relative to the selected continent
        console.log('changedContinent: ' + newContinent);
      }
    }
  }
</script>
Related