【问题标题】:Vue.js dynamic two way data binding between parent and child componentsVue.js 父子组件动态双向数据绑定
【发布时间】:2025-12-30 08:30:10
【问题描述】:

我正在尝试结合使用 v-for 和 v-model 来为某些输入表单获取双向数据绑定。我想动态创建子组件。目前,我没有看到子组件更新父数据对象。

我的模板是这样的

<div class="container" id="app">
  <div class="row">
    Parent Val
    {{ ranges }}
  </div>

   <div class="row">
     <button 
        v-on:click="addRange"
        type="button" 
        class="btn btn-outline-secondary">Add time-range
     </button>
    </div>

  <time-range 
    v-for="range in ranges"
    :box-index="$index"
    v-bind:data.sync="range">
  </time-range>

</div>

<template id="time-range">
  <div class="row">
    <input v-model="data" type="text">
  </div>
</template>

还有这个js

Vue.component('time-range', {
  template: '#time-range',
  props: ['data'],
  data: {}
})

new Vue({
  el: '#app',
  data: {
    ranges: [],
  },
  methods: {
    addRange: function () {
        this.ranges.push('')
    },
  }
})

我也做了一个 js fiddle https://jsfiddle.net/8mdso9fj/96/

【问题讨论】:

  • 您可以发出自定义事件以在 vue2 中将数据从子级发送到父级。你试过吗?
  • 特别是,您正在尝试使用 .sync 修饰符。您应该阅读how that works 的文档。
  • 大家好。我确实尝试了 .sync 修饰符,但没有任何运气jsfiddle.net/8mdso9fj/97
  • v-model 在道具上不起作用。您需要将其设置为发出 update:data 事件。

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


【解决方案1】:

注意:使用数组会使事情复杂化:您不能修改别名(v-for 变量)。

一种不经常提及的方法是catch the native input event as it bubbles up to the component。这可能比在链上传播 Vue 事件要简单一些,只要您知道组件中某处有一个元素发出原生 inputchange 事件。我在这个例子中使用了change,所以在你离开现场之前你不会看到它发生。由于数组问题,我必须使用splice 让 Vue 注意到元素的变化。

Vue.component('time-range', {
  template: '#time-range',
  props: ['data']
})

new Vue({
  el: '#app',
  data: {
    ranges: [],
  },
  methods: {
  	addRange: function () {
    	this.ranges.push('')
    },
  }
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="container" id="app">
  <div class="row">
    Parent Val
    {{ ranges }}
  </div>
  
   <div class="row">
     <button 
        v-on:click="addRange"
        type="button" 
        class="btn btn-outline-secondary">Add time-range
     </button>
    </div>

  <time-range 
    v-for="range, index in ranges"
    :data="range"
    :key="index"
    @change.native="(event) => ranges.splice(index, 1, event.target.value)">
  </time-range>
</div>

<template id="time-range">
  <div class="row">
    <input :value="data" type="text">
  </div>
</template>

要使用the .sync modifier,子组件必须发出update:variablename 事件,父组件将捕获并发挥其魔力。在这种情况下,变量名data。您仍然必须使用数组下标表示法,因为您仍然无法修改 v-for 别名变量,但是 Vue 对数组元素上的.sync 很聪明,所以没有乱七八糟的splice

Vue.component('time-range', {
  template: '#time-range',
  props: ['data'],
  methods: {
    emitUpdate(event) {
      this.$emit('update:data', event.target.value);
    }
  }
})

new Vue({
  el: '#app',
  data: {
    ranges: [],
  },
  methods: {
  	addRange: function () {
    	this.ranges.push('')
    },
  }
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div class="container" id="app">
  <div class="row">
    Parent Val
    {{ ranges }}
  </div>
  
   <div class="row">
     <button 
        v-on:click="addRange"
        type="button" 
        class="btn btn-outline-secondary">Add time-range
     </button>
    </div>

  <time-range 
    v-for="range, index in ranges"
    :data.sync="ranges[index]"
    :key="index">
  </time-range>

</div>

<template id="time-range">
  <div class="row">
    <input :value="data" type="text" @change="emitUpdate">
  </div>
</template>

【讨论】:

  • 谢谢@RoyJ!这就是为输入字段做的。我有一些其他自定义字段遇到了一些问题,但我通过使用 watch 并发出类似于常规文本输入的事件来解决它