【问题标题】:Custom Vue select component not updating selected option when v-model value changed当 v-model 值更改时,自定义 Vue 选择组件不更新所选选项
【发布时间】:2021-06-08 01:12:22
【问题描述】:

我正在尝试使用 v-model 模式将选择包装在 Vue 自定义组件中,如 in the docs 所述。

我面临的问题是我的自定义选择组件收到以下错误消息:

[Vue 警告]:避免直接改变一个 prop,因为它的值是 每当父组件重新渲染时被覆盖。相反,使用 基于道具值的数据或计算属性。道具存在 变异:“价值”

发现于

--->

但是,当我将 value 设为数据属性时,我失去了预期的功能。也就是说,当绑定值更改时,选择框不会更新。双向绑定丢失。

在不发出警告的情况下保持我所期望的行为的正确方法是什么?

这是一个演示问题的交互式示例(最好全屏显示)。

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

Vue.component('dynamic-select-ex2', {
  template: '#dynamic-select-template',
  props: ['options'],
  data() {
    return {
      value: null,
    }
  },
  methods: {
    changed() {
      // custom input components 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 },
      ]
    }
  },
  computed: {
   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.com/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 component. `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 component. `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>

【问题讨论】:

    标签: javascript vue.js vuejs2 vue-component vue-props


    【解决方案1】:

    计算二传手

    对于v-model,使用computed setterv-model 而不是value,以避免改变道具。 您可以摆脱 change 监听器。

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

    当计算值被访问时,它返回 prop 值,当它被设置时,它代替发射。

    或者:value@input$event.target.value

    另一种选择是从模板中对value$emit 进行单向绑定:

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

    【讨论】:

    • 谢谢!这与 v-model 在本机选择上的工作方式完全相同:)
    • 不客气 :) 我添加了另一个不使用 v-model 的选项
    • 第二个选项更简洁:)