Reputation: 2599
I'm having trouble getting two-way data binding to work in Vue 3 between a parent component and its child component, which is a form.
parent.vue
<template>
<h5>{{ record.title }}</h5>
<ChildForm v-model:record="record"></ChildForm>
</template>
<script lang="ts">
export default {
data() {
record: undefined
},
created() {
//code to fetch record
this.$data.record = new Record(responseJson);
},
watch: {
record: function(newRecord, oldRecord) {
alert("change detected!");
//perform appropriate logic when record is changed in child
}
}
}
</script>
child.vue
<template>
<InputText v-model="record.title"></InputText>
<InputText v-model="record.description"></InputText>
</template>
<script lang="ts">
export default {
props: {
record: {
type: Object as () => RecordModel
}
},
emits: ["update:record"]
}
If I change the title in the ChildForm's title input box, the parent accurately reflects the change in the h5 header. But, the watch function never gets fired.
What do I have to do to get the parent's watch method to fire when the child changes the model object's values?
Upvotes: 1
Views: 1918
Reputation: 2599
The answer Steven B linked to was correct but it took some work to adapt to Vue 3 and my case:
parent.vue
<template>
<h5>{{ record.title }}</h5>
<ChildForm v-model:record="record" v-on:update:record="parentRecordUpdate"></ChildForm>
</template>
<script lang="ts">
export default {
data() {
record?: undefined
},
created() {
//code to fetch record
this.$data.record = new Record(responseJson);
},
methods: {
parentRecordUpdate() {
alert("change detected!");
//perform appropriate logic when record is changed in child
}
}
}
</script>
child.vue
<template>
<InputText v-bind:value="record.title" @input="childUpdateRecord('title', $event.target.value)"></InputText>
<InputText v-bind:value="record.status" @input="childUpdateRecord('status', $event.target.value)"></InputText>
</template>
<script lang="ts">
export default {
props: {
record: {
type: Object as () => RecordModel
}
},
emits: ["update:record"],
methods: {
childUpdateRecord(field: string, value: string) {
//Update with whatever happened in the InputText box
this.$props.record.setAttribute(field, value);
//calling $emit triggers the parent's update method
this.$emit('update:record', this.$props.record);
}
}
}
Upvotes: 1
Reputation: 9362
A couple notes:
You're modifying the prop directly, which is frowned upon, and never technically emitting update:record
so the v-model is in essence, useless. If you were emitting, then the watch would fire as you would expect. If you want an example on how to do this, see this answer that shows the jist of it for vue2, you'll just need to modify it to use the new update:prop
event names.
If you want to keep modifying the prop directly, then you don't need the v-model:record
at all. Just use :record="record"
and make the watcher deep
watch: {
record: {
handler(newRecord, oldRecord) {
alert("change detected!");
},
deep: true
}
}
Upvotes: 1