I am trying to create a component that accepts an object as prop and can modify different properties of that object and return the value to the parent, using either sync or
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))
).
Another method without using props
could be to access the components attributes $attrs
in the child component.
In parent
<child v-model="parentObject" />
And in child, the value attribute generated by v-model
can be accessed in template as
$attrs.value.value1
And this is reactive.
Demo HERE
<TradeTableItem v-for="(debtReserve, index) in debtReserves" :key="debtReserve.id" :debtReserve="debtReserve" :market="market" :id="'order_by_' + index"></TradeTableItem>
In upper step, we generated id for each rows.
And in TradeTableItem (your template where we are populating, the table rows), Write id as :id="this.id" where this.id is a part of props.
Hope this helps
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}))
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.
It is not possible. There is a closed issue asking for it. This is the shortest method I found:
Script
Vue.component('child', {
template: `
<div>
<input :value="parent.foo" @change="$emit('update:parent', $event.target.value)" />
</div>
`,
props: {
parent: {
type: Object
}
}
})
new Vue({
el: '#app',
data: () => ({
parent: {foo: 'a', bar: 'b'}
}),
methods: {
mutate (value) {
this.parent.foo = value
}
}
})
Template
<div id="app">
{{parent}}
<child :parent="parent" @update:parent="mutate"></child>
</div>
https://jsfiddle.net/5k4ptmqg/475/