Vuejs : How to pass an object as prop and have the component update sub-objects

In #app, shoudld be parentObject, not parentValue.

In child, you had two inpyt, but you must have a single root element. In the example below I created a <div> root element for the component.

To update the parent, emit the events. This approach does not modify the parent's property in the child, so there's no breaking of the One-Way data flow.

Vue.component('child', {
  template: '#child',
  
  //The child has a prop named 'value'. v-model will automatically bind to this prop
  props: ['value']
});

new Vue({
  el: '#app',
  data: {
    parentObject: {value1: "1st Value", value2: "2nd value"}
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{parentObject}}</p>
  <child v-model="parentObject"></child>
</div>

<template id="child">
<div>
   <input type="text" v-bind:value="value.value1" v-on:input="$emit('input', {value1: $event.target.value, value2: value.value2})">
   <input type="text" v-bind:value="value.value2" v-on:input="$emit('input', {value1: value.value1, value2: $event.target.value})">
</div>
</template>

About the <input>s: you can bind each to a property of the parent's value. Then, when edited, emit an event modifying just that property (v-on:input="$emit('input', {value1: $event.target.value, value2: value.value2})) and keeping the other's value. The parent updates in consequence.

If you have many properties, you can replace in the second input, for example:

$emit('input', {value1: value.value1, value2: $event.target.value})

With

$emit('input', Object.assign({}, value, {value2: $event.target.value}))


Using a method instead of emitting directly from the template

I kept the previous demo because it is more direct and simpler to understand (less changes from your original code), but a more compact approach would be to use a method (e.g. updateValue) and reuse it in the template:

Vue.component('child', {
  template: '#child',
  
  //The child has a prop named 'value'. v-model will automatically bind to this prop
  props: ['value'],
  methods: {
    updateValue: function(propertyName, propertyValue) {
      this.$emit('input', Object.assign({}, this.value, {[propertyName]: propertyValue}))
    }
  }
});

new Vue({
  el: '#app',
  data: {
    parentObject: {value1: "1st Value", value2: "2nd value"}
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>

<div id="app">
  <p>Parent value: {{parentObject}}</p>
  <child v-model="parentObject"></child>
</div>

<template id="child">
<div>
  <input type="text" v-bind:value="value.value1" v-on:input="updateValue('value1', $event.target.value)">
  <input type="text" v-bind:value="value.value2" v-on:input="updateValue('value2', $event.target.value)">
</div>
</template>

The demo above, as you can see, already uses Object.assign(), meaning it will handle an indefinite number of properties.


You shouldn't modify the object being passed in as a prop. Instead, you should create a new data property in the child component and initialize it with a copy of the prop object.

Then, I would just use a v-model for each of the inputs in the child component and add a deep watcher to the internal value which would emit an update whenever the internal value changes.

Vue.component('child', {
  template: '#child',
  props: ['value'],
  data() {
    return { val: {...this.value} };
  },
  watch: {
    val: {
      deep: true,
      handler(value) {
        this.$emit('input', value);
      }
    }
  }
});

new Vue({
  el: '#app',
  data: {
    parentObject: {value1: "1st Value", value2: "2nd value"}
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>

<div id="app">
  <p>Parent value: {{parentObject}}</p>
  <child v-model="parentObject"></child>
</div>

<template id="child">
  <div>
    <input type="text" v-model="val.value1">
    <input type="text" v-model="val.value2">
  </div>
</template>

I made a shallow copy of the prop in my example ({...this.value}), because the object doesn't have any nested properties. If that wasn't the case, you might need to do a deep copy (JSON.parse(JSON.stringify(this.value))).

Tags:

Vue.Js

Vuejs2