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 emit events. The example won't work, but it's simply to demonstrate what I'm trying to achieve.
Here's a snippet of what I'm trying to achieve :
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 (value) {
//Not sure if both fields should call the same updateValue method that returns the complete object, or if they should be separate
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.js"></script>
<div id="app">
<p>Parent value: {{parentObject}}</p>
<child v-model="parentObject"></child>
</div>
<template id="child">
<input type="text" v-bind:value.value1="value" v-on:input="updateValue($event.target.value)">
<input type="text" v-bind:value.value2="value" v-on:input="updateValue($event.target.value)">
</template>
To pass in the properties of an object as props, we can use the v-bind without the argument. Then the properties of post will be passed into blog-post as prop values. The property names are the prop names.
As long as you're updating a reactive property (props, computed props, and anything in data ), Vue knows to watch for when it changes. All we have to do is update count , and Vue detects this change. It then re-renders our app with the new value!
It's a pretty common question that newer Vue developers often ask. You can pass strings, arrays, numbers, and objects as props. But can you pass a function as a prop? While you can pass a function as a prop, this is almost always a bad idea.
We can pass props to dynamic components using the v-bind directive. To display dynamic components, we add the component component with the is prop. currentComponent is a string with the component name. v-bind has an object with the props that we want to pass to whatever component is being rendered now.
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))
).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With