最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Custom Vue select component not updating selected option when v-model value changed - Stack Overflow

programmeradmin1浏览0评论

I'm trying to wrap a select in a Vue custom ponent using the v-model pattern as described in the docs.

The problem I'm facing is that I get the following error message for my custom select ponent:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value. Prop being mutated: "value"

found in

--->

However, when I make value a data property, I loose the expected functionality. That is, when the bound value changes, the select box doesn't update. The two-way binding is lost.

What is the correct way to maintain the behavior I am expecting without raising warnings?

Here is an interactive example demonstrating the problem (best seen in full screen).

Vueponent('dynamic-select-ex1', {
  template: '#dynamic-select-template',
  props: ['value', 'options'],
  methods: {
    changed() {
      // custom input ponents need to emit the input event
      this.$emit('input', event.target.value)
    },
  },
})

Vueponent('dynamic-select-ex2', {
  template: '#dynamic-select-template',
  props: ['options'],
  data() {
    return {
      value: null,
    }
  },
  methods: {
    changed() {
      // custom input ponents need to emit the input event
      this.$emit('input', event.target.value)
    },
  },
})

let example = new Vue({
  el: '#example',
  data() {
    return {
      selected: null,
      options: [
        { text: 'Hello', value: 1 },
        { text: 'World', value: 2 },
        { text: 'Blah', value: 3 },
        { text: 'Blerg', value: 4 },
      ]
    }
  },
  puted: {
   text() {
     if (!this.selected) return
     return this.options.find(({ value }) => value == this.selected).text
   },
  },
  methods: {
    select(value) {
      this.selected = value
    }
  }
})
<script src=".5.17/vue.js"></script>

<script type="text/x-template" id="dynamic-select-template">
  <select v-model="value" @change="changed">
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select>
</script>

<div id="example">
  <label for="direct">Vue behaviour for native select</label><br>
  <select id="direct" v-model="selected">
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select><br>

  <div>Vue behaviour for custom ponent. `value` is a prop. Warning output in console when user selects option</div>
  <dynamic-select-ex1 v-model="selected" :options="options"></dynamic-select-ex1><br>

  <div>Vue behaviour for custom ponent. `value` is a data property. two-way binding is broken.  Selected option not updated when `value` changes.</div>
  <dynamic-select-ex2 v-model="selected" :options="options"></dynamic-select-ex2><br>
  
  <br>Selected: {{ text }}<br><br>
  
  <button @click="select(1)">Hello</button>
  <button @click="select(2)">World</button>
  <button @click="select(3)">Blah</button>
  <button @click="select(4)">Blerg</button><br>

</div>

I'm trying to wrap a select in a Vue custom ponent using the v-model pattern as described in the docs.

The problem I'm facing is that I get the following error message for my custom select ponent:

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent ponent re-renders. Instead, use a data or puted property based on the prop's value. Prop being mutated: "value"

found in

--->

However, when I make value a data property, I loose the expected functionality. That is, when the bound value changes, the select box doesn't update. The two-way binding is lost.

What is the correct way to maintain the behavior I am expecting without raising warnings?

Here is an interactive example demonstrating the problem (best seen in full screen).

Vue.ponent('dynamic-select-ex1', {
  template: '#dynamic-select-template',
  props: ['value', 'options'],
  methods: {
    changed() {
      // custom input ponents need to emit the input event
      this.$emit('input', event.target.value)
    },
  },
})

Vue.ponent('dynamic-select-ex2', {
  template: '#dynamic-select-template',
  props: ['options'],
  data() {
    return {
      value: null,
    }
  },
  methods: {
    changed() {
      // custom input ponents need to emit the input event
      this.$emit('input', event.target.value)
    },
  },
})

let example = new Vue({
  el: '#example',
  data() {
    return {
      selected: null,
      options: [
        { text: 'Hello', value: 1 },
        { text: 'World', value: 2 },
        { text: 'Blah', value: 3 },
        { text: 'Blerg', value: 4 },
      ]
    }
  },
  puted: {
   text() {
     if (!this.selected) return
     return this.options.find(({ value }) => value == this.selected).text
   },
  },
  methods: {
    select(value) {
      this.selected = value
    }
  }
})
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.5.17/vue.js"></script>

<script type="text/x-template" id="dynamic-select-template">
  <select v-model="value" @change="changed">
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select>
</script>

<div id="example">
  <label for="direct">Vue behaviour for native select</label><br>
  <select id="direct" v-model="selected">
    <option v-for="option in options" :value="option.value">{{ option.text }}</option>
  </select><br>

  <div>Vue behaviour for custom ponent. `value` is a prop. Warning output in console when user selects option</div>
  <dynamic-select-ex1 v-model="selected" :options="options"></dynamic-select-ex1><br>

  <div>Vue behaviour for custom ponent. `value` is a data property. two-way binding is broken.  Selected option not updated when `value` changes.</div>
  <dynamic-select-ex2 v-model="selected" :options="options"></dynamic-select-ex2><br>
  
  <br>Selected: {{ text }}<br><br>
  
  <button @click="select(1)">Hello</button>
  <button @click="select(2)">World</button>
  <button @click="select(3)">Blah</button>
  <button @click="select(4)">Blerg</button><br>

</div>

Share Improve this question edited Jul 14, 2022 at 2:36 tony19 139k23 gold badges277 silver badges347 bronze badges asked Mar 10, 2021 at 5:29 br3ntbr3nt 9,6263 gold badges46 silver badges67 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 7

Computed Setter

For v-model, use a puted setter with v-model instead of value, to avoid mutating the prop. You can get rid of the change listener.

<select v-model="model">
  <option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>
Vue.ponent('dynamic-select-ex1', {
  template: '#dynamic-select-template',
  props: ['value', 'options'],
  puted: {
    model: {
      get() { return this.value },
      set(value) { this.$emit('input', value) }
    }
  }
})

When the puted value is accessed, it returns the prop value, and when it's set, it emits instead.

Or :value, @input, and $event.target.value

Another option is to make a 1-way binding of value and $emit from the template:

<select :value="value" @input="$emit('input', $event.target.value)">
  <option v-for="option in options" :value="option.value">{{ option.text }}</option>
</select>
Vue.ponent('dynamic-select-ex1', {
  template: '#dynamic-select-template',
  props: ['value', 'options'],
})
发布评论

评论列表(0)

  1. 暂无评论