[Core Team RFC] New SFC macro: defineModel #503
Replies: 42 comments 99 replies
-
Could we also consider doing the same for the Options API (which is still a popular way of writing components)? export default {
models: [
'foo', // name: 'foo', event: 'update:foo', required: false, default: undefined
{ name: 'value', event: 'updateValue', default: 0 },
{ name: 'bar', required: true }, // event: 'update:bar'
],
props: {
foo: String, // error: model is already declared
},
} We could then (finally) close #140. |
Beta Was this translation helpful? Give feedback.
-
What are the use cases for |
Beta Was this translation helpful? Give feedback.
-
Very nice, it reminds me of how Aurelia just lets you write back into props to implement two-way bindings, great DX.
Speaking of |
Beta Was this translation helpful? Give feedback.
-
Examples show only usage with primitive types, how will it work with object props? Will it force (typewise) a factory for the default? const count = defineModel<{ foo: string }>('count', {
default: () => ({ foo: '' })
}) Will it emit? I guess at this stage no, but would you consider adding a const ob = defineModel({ foo: string }>();
ob.value.foo = 'foo'; // or <input v-model="ob.foo"> |
Beta Was this translation helpful? Give feedback.
-
I love this proposal for simple/standard cases because it can eliminate a bunch of boilerplate code, but I also am not a fan of the models now disappearing from the props definitions. I'm assuming (there's no example showing what it compiles down to, so I can't be certain) that I can still access the prop via prop.modelValue even if modelValue isn't defined in defineProps, right? In complex cases, where I need to watch the prop and not update the local ref in certain conditions, there's no way to hook into this, so I'd have to skip using this, right? |
Beta Was this translation helpful? Give feedback.
-
TS error "vue": "^3.3.1",
"vite": "^4.3.5", Type '{ defineModel: true; }' is not assignable to type 'Partial<Pick<SFCScriptCompileOptions, "babelParserPlugins">>'.
Object literal may only specify known properties, and 'defineModel' does not exist
in type 'Partial<Pick<SFCScriptCompileOptions, "babelParserPlugins">>' |
Beta Was this translation helpful? Give feedback.
-
export default defineConfig(
{
//... rest of configurations
plugins: [
vue({
script: {
defineModel: true,
propsDestructure: true,
},
}),
//... rest of plugins
]
} |
Beta Was this translation helpful? Give feedback.
-
Why not expose this idea as writable props? It's pretty simple to understand conceptually ( It would look something like: export default {
props: {
// New prop attribute: "model: true" or maybe "writable: true"
value: { type: Number, model: true },
},
setup(props) {
props.value = 100; // emits "update:value"
}
} |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
defineModel default has a "wrong" typescript definition. export function defineModel<T>(
options: { default: any } & PropOptions<T> & DefineModelOptions
): Ref<T> wouldn't it be better to just leave this out, so that the correct definition of PropOptions can be used ? |
Beta Was this translation helpful? Give feedback.
-
Anyone get this working in Storybook 7? Overriding the |
Beta Was this translation helpful? Give feedback.
-
Nothing much to add to the discussion other than I'm finding |
Beta Was this translation helpful? Give feedback.
-
should defineModel suppose to have a deep mode? It is convenient to bind vmodel with items of an array or object in v-for, for now, changes on item doesn't emit model event. |
Beta Was this translation helpful? Give feedback.
-
Might've asked this sometime before, can't rememeber when or where though, but when will this become stable? I'm just hoping it works by the time my code goes into production sometime in the distant future. |
Beta Was this translation helpful? Give feedback.
This comment was marked as disruptive content.
This comment was marked as disruptive content.
-
It's definitely a very nice feature idea, but I think it has its drawbacks :
I think that something is missing to be very easy to understand for a newcomer. If we had different semantics for :
We could argue that this is, somehow, what we are doing here, but I feel like it's partially wrong: It would require a In an utopic world, without the need for backward compatibility, it would even make sense to me to not be able to define an emit such as Anyway, very interesting feature, that I will definitely use :). I'm not sure why it made me wonder about all this ^^. |
Beta Was this translation helpful? Give feedback.
-
How could this be confusing for newcomers? It's super easy, it's just a reference to the variable in the vmodel. Like writable props. Using emits is way harder far more unintuitive. And overuse? You either need it or you don't, how could you possibly use when you're not supposed to? |
Beta Was this translation helpful? Give feedback.
-
i recently come back learn coding and i pick vue, I'm quite headache with the how to create my custom input component. End up I use emit, then notice that definemodel is helpful. as user who not familiar with vue my first issue is props is immutable, is that anyway can use syntax like below? props = defineProps<{prop1:string, modelValue:{type:string, changable:true}}> then you guys run 'defineModel' behind the scene? Or there is another approach i feel workable: |
Beta Was this translation helpful? Give feedback.
-
How does defineModel handle v-model modifiers? Are v-model modifiers never used with defineModel? |
Beta Was this translation helpful? Give feedback.
-
Is it possible to use defineModel inside a component with v-for? <SchemaCollectionProvider v-for="schema in schemas" v-model:schema="schema" :key="schema.id">
<SchemaCard :schema="schema'/>
</SchemaCollectionProvider>
This is really simplified of course. Would the only option be to use a prop with event and do this? <script setup lang=ts>
const schemas = ref<SchemaCollectionModel[]>([])
function updateValue(index: number, schema: SchemaCollectionModel) {
schemas.value.splice(index, 1, schema)
}
</script>
<template>
<SchemaCollectionProvider v-for="(schema, i) in schemas" :schema="schema" :key="schema.id" @update:schema="(event) => updateValue(i, event)">
<SchemaCard :schema="schema">
</SchemaCollectionProvider>
</template> |
Beta Was this translation helpful? Give feedback.
-
@yyx990803 or somebody at Vue, could we please get an update on the status of this feature? It's been experimental for a long time now and we have not heard anything about it's future. There have been many patch updates since v3.3. I assume if it is kept we'll have to wait for v3.4? |
Beta Was this translation helpful? Give feedback.
-
I just want to share my feedback about this feature. Our team has been using it for about two months and it does make using v-model much more enjoyable, looking back defining props/emits before feels like boilerplate. I guess the only concern I have is that we always export our props interface, so other component consumers can extend it if needed. Since defineModel is separate thing it sometimes easy to miss, but you can still define modelValue prop and they do not clash, so that isn't major issue. |
Beta Was this translation helpful? Give feedback.
-
Overall, I think this is a nice addition. There are a few minor inconsistencies that can occur in local mode. Whether these are worth fixing I'm not sure, but if not then they might impact how the feature is documented. I think people will perceive The relevant Vue code that leads to these differences can be seen here: Problem 1Consider this example: This uses If you change the example to Perhaps the local watcher should use Problem 2The same example shows the second problem. This time, take a look at the value for With It could be argued that this is not an inconsistency, it's just what the Problem 3This is the flip-side of the previous problem. Consider this example: The parent attempts to apply upper and lower bounds to the value. With Just to reiterate, I'm not necessarily suggesting that these inconsistencies need fixing, but I do think they're worth pondering. It might impact the documentation if nothing else. |
Beta Was this translation helpful? Give feedback.
-
To enable it in quasar, add the following line to quasar.config.js:
|
Beta Was this translation helpful? Give feedback.
-
If it's not a props:{
foo: {tyep: Number}
} |
Beta Was this translation helpful? Give feedback.
-
How do I approach this case where when I For example, I have a component that I need both the current and previous value because I have to compare them before doing other things. So I have this kind of component:
I also have a case where it isn't necessarily a previous value, but some other data. If I've approached this solution wrong even in the previous way, please correct me. |
Beta Was this translation helpful? Give feedback.
-
I'm having an issue with getting the same behaviour that I got with |
Beta Was this translation helpful? Give feedback.
-
Congratulations on the release of Vue 3.4! I find defineModel to be a very useful feature. However, I've also realized that defineModel can be props with non-one-way data flow. Consequently, it's important to encourage developers not to use v-model too much for data exchange between parent and child components. |
Beta Was this translation helpful? Give feedback.
-
Using defineModel with an array does not seem to work when splicing. model.value.splice(idx, 1) does not update the model. Is this intended?? |
Beta Was this translation helpful? Give feedback.
-
I have a small issue with the trim Modifier: ExampleParent<script setup>
import { ref } from 'vue'
import Child from 'Child.vue'
const testModel = ref("foo bar")
</script>
<template>
<Child v-model.trim="testModel" />
</template> Child<script setup>
const model = defineModel()
</script>
<template>
<input v-model="model" />
</template> If I Backspace 'bar' the cursor will jump to the end of 'foo' -- I would expect the result to be 'foo ' until blur. I've tried this in Playground with the same result. |
Beta Was this translation helpful? Give feedback.
-
defineModel
macro anduseModel
helper core#8018Summary
This proposal introduces a new SFC macro called
defineModel
that enhances the developer experience when declaring two-way binding props to be consumed byv-model
.With
defineModel
, v-model props can be declared and mutated like a ref.<script setup>
only feature.update:propName
event.defineModel
, and it's disabled by default.Basic example
Comp.vue
Parent.vue
Motivation
Detailed design
If the first argument is a string, it will be used as the prop name; Otherwise the prop name will default to "modelValue". In both cases, you can also pass an additional object which will be used as the prop's options.
Local Mode
The options object can also specify an additional option,
local
. When set totrue
, the ref can be locally mutated even if the parent did not pass the matchingv-model
, essentially making the model optional.Demo
Drawbacks
N/A
Alternatives
defineModels
Declares multiple models in the same macro call. We opted for the single-model API for two reasons:
It is more common for a component to declare only one model binding.
The default usage
defineModel()
, which omits the prop name, aligns with the parent usage ofv-model
, which omits the argument. This completely removes the need for themodelValue
prop name convention.Adoption strategy
This is implemented and shipped as an experimental feature in 3.3 beta and requires explicit opt-in.
Vite
vue-cli
Requires
vue-loader@^17.1.1
Beta Was this translation helpful? Give feedback.
All reactions