Not sure how to properly approach defineModel with objects #10538
Replies: 9 comments 6 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.
-
Hello.
I've been experimenting with defineModel quite a bit lately and find it super neat and handy, however, not sure how to properly use it if we're dealing with objects instead of primitives.
Example: https://play.vuejs.org/#eNqFUlFPwjAQ/itNX9AEt4g+zUGiBqMmilEf+zK2A4pdW9sO0WX/3esGOETkqe333V2/u/tKeql1sCiARjS2qeHaEQuu0EQkctpn1FlGB0zyXCvjSEkMTEhFJkblpINpnQ11o9S1wqsE6VZ8ELZB/0vngkkmUyUtxihF+r7eUenvETntknFiItLz51eEn23D+G91jAXisBGKsvDhINcicYAvQuItFYuTXGUgsAssxCgJMSYOWwm0i/2hmgmfBnOrJA6h9GUYTbEIF2BG2nFUyyjK8YznEiHUx32NOVNAd42nM0jf/sDndukxRp8MWDALYHTDucRMwTX08OURlnjfkKi+EBj9D/kMVonCa2zCrgqZoexWXK32rt4Rl9NXO1w6kHbdlBfqI6s6nlHckZ/fvtZ/5J4F53UekxVO8feeD7ipMUC9HLRABhMu4cG/0AoG3gtuIFsNkbhPDREZjeeQusP751IX7cXXZ9CsH8NSmCmRgdmyxN40tN1OWoP5tDpxdjq4BZxw43isGYcINVRvUJarJqsK8d6O/apvuZsr/w==
So, update:modelValue not emitted from the child and the nested properties get directly mutated without any error. I expected the event to be emitted and the reference to foo in parent to be updated like when dealing with primitives but seems that's not the case for objects and arrays. How do you guys approach this?
I can even do smth like this and it will work which looks wrong to me:
https://play.vuejs.org/#eNqFUk2P0zAQ/SuWLwWpm2gLp5CtBGgRIMEi4OhLNpm2Lq5t/FEKUf47z0632+6ncoj93sz4zczr+Vtri20kXvHat07awDyFaJlq9PJC8OAFnwstN9a4wHrmaMEGtnBmwyZImxyoD8a8Nzhq0mHPF+UxmF6ZvBE6fa3RHlHGsItU8UWfzhU7n7LrxlVslv7/Kjx3CuPl4aXQdTkqhS5cAm2sagLhxlh9IqPamI7U2bZRkdALignOSgTW5VEWn6JLKFrIZbH2RmMUfaoleItKUpG7skFCseCQlJjENUqZP58zFlyk6Q3erqj99QC+9ruECf7NkSe3JcEPXGjcksJIX/74SjucDyR6iArRT5DfyRsVk8Yx7F3UHWQfxWW1n/KmpF7+9Je7QNrfNJWEpsghxwuOTaUhPtb6rdxXxeucJ/SAKd7d9jOeGk2QVwQbdLSQmr6kG+zg6HeUjrr9EFn4a6liV9dragM8ABs9ZQKpbQxse5Zr48H8L8b1I6yllVEduRNLPJoG691LG7GUlhNX5/OPhAmPvkfNugQ0UrN53++bHAbgs3v2G/4DBT0ubw==
Beta Was this translation helpful? Give feedback.
All reactions