Not sure how to properly approach defineModel with objects #10538
-
Beta Was this translation helpful? Give feedback.
Replies: 10 comments 7 replies
-
I have the same problem (I don't have an ideal solution but I can emit events..). Just like you, I want to define an Object type model with defineModel, but using defineModel like this: export type Model = {
a: string;
b: string;
} <script setup lang="ts">
import GrandChild from "./GrandChild.vue";
import type { Model } from "./type"
const model = defineModel<Model>()
</script>
<template>
<div>
<h3>ChildIdeal Component</h3>
a: <GrandChild v-model="model.a" /><br />
b: <GrandChild v-model="model.b" />
</div>
</template> (GrandChild is a simple wrapper for input. See the link above.) GrandChild's v-model does NOT emit Child's update:modelValue so it means the object(=prop) is mutated. In Vue devtools, there is only one emit which is from GrandChild. So, I had to write like this: <script setup lang="ts">
import GrandChild from "./GrandChild.vue";
import type { Model } from "./type"
const model = defineModel<Model>()
const onUpdateA = (a: string) => {
model.value = {...model.value, a}
}
const onUpdateB = (b: string) => {
model.value = {...model.value, b}
}
</script>
<template>
<div>
<h3>ChildActual Component</h3>
a: <GrandChild :model-value="model.a" @update:model-value="onUpdateA" /><br />
b: <GrandChild :model-value="model.b" @update:model-value="onUpdateB" />
</div>
</template> In Vue devtools, there are two emits which are from Child and GrandChild. Declaring and using onUpdate function and binding model-value / @update:model-value is a tricky and reluctant solution... We need a more simple and clear solution. |
Beta Was this translation helpful? Give feedback.
-
Is mutating the prop value not considered bad practice anymore? If the latter, I think a lot of buggy code will be written. I assumed that, internally, defineModel would call an "update:modelValue" event, with an updated copy, whenever we change a property value . I think a lot of other people assume the same. Maybe defineModel should return ModelRef< |
Beta Was this translation helpful? Give feedback.
-
I would also like to use To avoid these pitfalls should I do something like this? Not saying this is the solution, just wondering if this would avoid the problem until we have something better. And here is the code from that example: <template>
Full Name Input in Child: <input v-model="localForm.name.fullName" />
</template>
<script setup lang="ts">
import { reactive, watch } from "vue";
// Making a form object with nested properties
interface ContactForm {
name: {
fullName: string
}
}
// Creating the parnet/child component v-model sync using defineModel
const contactForm = defineModel<ContactForm>({
required: true,
});
// Making a copy of contactForm to avoid mutating it's properties
const localForm = reactive<ContactForm>({ ...contactForm.value });
// Watch for changes in localForm and update the contactForm to keep it in sync with parent
watch(
() => localForm,
() => {
contactForm.value = { ...localForm };
},
{ deep: true },
);
</script> |
Beta Was this translation helpful? Give feedback.
-
Up, having the same issue |
Beta Was this translation helpful? Give feedback.
-
I caught this myself when tried to use shallowRef in parent. Vue documentation should be updated I was absolutely certain that define model creates a reactive copy of the passed prop. So that when I did modelValue.someField = value it would emit the new copy of the object not perform the actual direct mutation. |
Beta Was this translation helpful? Give feedback.
-
I have discussed this issue on a stack overflow. The best suggestion I can make at this point is to use the similar but more feature rich I show a strategy over here: https://stackoverflow.com/questions/79042840/vue-v-model-with-objects#answer-79047600 in brief: const emit = defineEmits(['update:foo'])
const foo = useVModel(props, 'foo', emit, { deep: true, passive: true }) this emits correctly when changing deep properties of an object/array. it uses watchers under the hood so take that into consideration. |
Beta Was this translation helpful? Give feedback.
-
It's a real shame there's no out of the box support for this, I ended up doing this The tab-ing between elements is still glitchy but it's working even if I shouldn't modify props. |
Beta Was this translation helpful? Give feedback.
-
I made a proposal for supporting it out of the box (with a solution which you can copy-paste): vuejs/rfcs#725 |
Beta Was this translation helpful? Give feedback.
-
Is there any progress to use defineModel with objects out of the box? |
Beta Was this translation helpful? Give feedback.
No. We have 2 options: mutating nested properties of props directly or updating reference to the defineModel's model by shallow copying the original model and reassigning it.
I prefer the 1st option since it is cheaper and less boilerplate'y though against vue principals of not mutating props.