Apr 28, 2023 · 43 comments · 101 replies
{{actor}} deleted this content .
-
defineModel
macro and useModel
helper core#8018This proposal introduces a new SFC macro called defineModel
that enhances the developer experience when declaring two-way binding props to be consumed by v-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.Comp.vue
<script setup> const modelValue = defineModel() console.log(modelValue.value) </script> <template> <input v-model="modelValue"> </template>
Parent.vue
<script setup> import { ref } from 'vue' import Comp from './Comp.vue' const msg = ref('') </script> <template> <Comp v-model="msg"> </template>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.
// default model (consumed via `v-model`) const modelValue = defineModel() // ^? Ref<any> modelValue.value = 10 const modelValue = defineModel<string>() // ^? Ref<string | undefined> modelValue.value = "hello" // default model with options, required removes possible undefined values const modelValue = defineModel<string>({ required: true }) // ^? Ref<string> // with specified name (consumed via `v-model:count`) const count = defineModel<number>('count') count.value++ // with specified name and default value const count = defineModel<number>('count', { default: 0 }) // ^? Ref<number>Local Mode
The options object can also specify an additional option, local
. When set to true
, the ref can be locally mutated even if the parent did not pass the matching v-model
, essentially making the model optional.
// local mutable model, can be mutated locally // even if the parent did not pass the matching `v-model`. const count = defineModel<number>('count', { local: true, default: 0 })Drawbacks
N/A
AlternativesDeclares 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 of v-model
, which omits the argument. This completely removes the need for the modelValue
prop name convention.
This is implemented and shipped as an experimental feature in 3.3 beta and requires explicit opt-in.
Vite// vite.config.js export default { plugins: [ vue({ script: { defineModel: true } }) ] }vue-cli
Requires vue-loader@^17.1.1
// vue.config.js module.exports = { chainWebpack: (config) => { config.module .rule('vue') .use('vue-loader') .tap((options) => { return { ...options, defineModel: true } }) } }
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
All reactions-
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.
You must be logged in to vote
3 replies
{{actor}} deleted this content .
-
That would be a separate proposal - this RFC is strictly scoped to <script setup>
.
Beta Was this translation helpful? Give feedback.
-
That would be a separate proposal - this RFC is strictly scoped to SFCs.
Options API is available in SFCs. You mean it's only available for <script setup>.
Beta Was this translation helpful? Give feedback.
-
What are the use cases for local: false
? Would not be simplier to have local: true
by default?
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
7 replies
-
but if you provide a default value, you don't need required: true, do you? 🤔
Beta Was this translation helpful? Give feedback.
-
@posva I recall for TS typing pre-setup and generics, you did, because required
decides whether the type can be undefined
.
// props.n: number | undefined = 0; const props: { n: { type: Number, default: 0 } } // props.n: number = 0; const props: { n: { type: Number, required: true, default: 0 } }
As I said, maybe that's not super relevant for newer APIs, as in script setup I would do:
// This follows my TS generic type: // n: number // m: number | undefined const { n = 0, m = 0 } = defineProps<{ n: number, m: number | undefined, }>();
Beta Was this translation helpful? Give feedback.
-
In what situation would a prop == undefined if a default is set?
Beta Was this translation helpful? Give feedback.
-
@joezimjs nothing prevents you to explicitly bind an undefined
value.
Beta Was this translation helpful? Give feedback.
-
@jods4 I'm glad the required
is no longer necessary now though 😆
A prop that accepts undefined
will still take the default value if explicitly set to undefined
as it's the same as not being passed at all Example
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.
local: true
is a really nice addition, because it's a fair amount of boilerplate to create a control that maintains state both when it's databound and when it's not.
Can you provide some details about the design decisions that went into it?
hasChanged
) no event is raised (just like refs don't trigger)?Speaking of hasChanged
, this is outside the scope of this SFC but Vue is lacking a way to extend its behaviour (hard-coded to Object.is
). For example, when using a date library such as Dayjs it is natural to treat same dates (but different objects) as equal. Many other frameworks have this, for example Lit has a hasChanged
option when declaring reactive properties that you can define on a per-property basis.
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
1 reply
-
Is it implemented inside the core props code or as an extra layer
As an extra layer, with an additional ref and watchers. It works very similar to useVModel
from VueUse.
If it's an extra ref layer, does it exist always or is the component checking whether the component is two-way bound?
It always exists if local: true
is specified. Which is why it is false
by default.
I assume that when model is assigned the same value (according to hasChanged) no event is raised (just like refs don't trigger)?
Correct.
Vue is lacking a way to extend
hasChanged
behaviour
An RFC is welcome. There are various aspects that need discussion. This can affect watchers, computed, and refs. Introducing extra options for each feels heavy handed, but a global option isn't flexible enough.
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 deep
option (like in useVModel
from vueuse
)?
const ob = defineModel({ foo: string }>(); ob.value.foo = 'foo'; // or <input v-model="ob.foo">
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
4 replies
{{actor}} deleted this content .
-
It does not emit, although if the object is deeply reactive then direct mutations should just trigger updates in parent without even emitting.
Beta Was this translation helpful? Give feedback.
-
direct mutations should just trigger updates in parent without even emitting.
I'm fully aware of that, but I was always under the imporession that mutating prop's properties is not the best practice and you'd rather emit a new object with updated property. Not actually sure whether deep
option is actually doing what I think it's doing
Beta Was this translation helpful? Give feedback.
-
without even emitting
that's the point, this behavior breaks the consistency, some of the v-model
's emit events, others not. I always end up defining a local state (based on the passed modelValue), mutating it and emitting to the parent as object. Although this generates boilerplate code, it ensures consistent behavior across all components. I think we still miss a better approach for this use case
Beta Was this translation helpful? Give feedback.
-
Wait, an object prop's changes don't emit? 🤔 Seems like sticking with VueUse is the better option then.
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.
You must be logged in to vote
2 replies
-
Correct. If you want this level of control, just roll it yourself.
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
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.
You must be logged in to vote
1 reply
-
export default defineConfig( { //... rest of configurations plugins: [ vue({ script: { defineModel: true, propsDestructure: true, }, }), //... rest of plugins ] }
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
All reactions7 replies
{{actor}} deleted this content .
-
SolvedOn nuxt.config.ts:
vite: { vue: { script: { defineModel: true, }, }, },
I've been searching for ages! Discord, YouTube, Google, even ChatGPT!!! Can't get this to work on Nuxt 3 or Vue.
Already installed all latest and beta versions of packages like vue, nuxt, @vue/compiler-sfc, vite. Attempted to override the packages in the package.json. defineModel is undefined when compiled to JS.The one in the macros library works fine: https://vue-macros.sxzz.moe/macros/define-models.html
Beta Was this translation helpful? Give feedback.
-
Just an aside, there is no way stock ChatGPT is going to be helpful about this: it's training data only goes up to 2021.
Beta Was this translation helpful? Give feedback.
-
In a plain Vite project, updating vue
and @vitejs/plugin-vue
solved my issue.
For Nuxt users, it seems like you have to wait the soon-to-be-released Nuxt 3.5.
Beta Was this translation helpful? Give feedback.
-
I got it working thanks to someone in Discord. Updated my reply.
Beta Was this translation helpful? Give feedback.
-
Why not expose this idea as writable props?
It's pretty simple to understand conceptually (defineModel
defines a prop + an event, after all) and it works in classical <script>
as well.
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.
You must be logged in to vote
8 replies
{{actor}} deleted this content .
-
From my personal experience with frameworks where it works like that, I never found it confusing nor have I seen someone say so.
In particular with defineProps
destructuring experiment, I'd say this proposal is not any more hidden than defineModel
.
At assignment site, both a destructured defineProps
and defineModel
would look identical, they're simple variable assignments. There's no telling sign that one raises an event and the other does not.
At declaration, I'd say that { model: true }
is as explicit as definedModel
, it is a simple marker with a clear meaning.
EDIT: I just realised the assignment site remark is is not 100% accurate. defineModel
returns a ref so it'd be model.value = 1
, whereas destructured props is just model = 1
.
I'd see this as a benefit because just as we make one step towards plain variable for props, we're making one step back to ref and .value
for models.
On the other hand, if the destructured props experiment is eventually dropped, assigning to props
variable such as props.model = 1
is a very clear sign that you're sending an event, as props are otherwise be read-only.
Beta Was this translation helpful? Give feedback.
-
Why not expose this idea as writable props?
I think it would make way more sense if it's all together instead of separated into two separate functions, and models are props anyway. though my idea was to have the options also be part of the type definition, if that's possible.
Beta Was this translation helpful? Give feedback.
-
defineModel
returns a ref so it'd bemodel.value = 1
, whereas destructured props is justmodel = 1
But if it's model = 1
it can't be reactive? It can only be props.model = 1
or model.value = 1
, there has to be some kind if property for it to be reactive/ref.
Beta Was this translation helpful? Give feedback.
-
But if it's
model = 1
it can't be reactive? It can only beprops.model = 1
ormodel.value = 1
, there has to be some kind if property for it to be reactive/ref.
There's an experimental feature in Vue 3.3 (has to be enabled with an option) that makes destructured props "magically" reactive.
// !! experimental, not yet for prod !! // This code in your component: let { model } = defineProps(...); const x = model; model = 42; // Is transformed by compiler into const x = props.model; props.model = 42;
Beta Was this translation helpful? Give feedback.
-
Personally I'm a fan of mutable props.
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
-
defineModel default has a "wrong" typescript definition.
why is the default set to the type any in apiSetupHelpers.ts ?
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.
You must be logged in to vote
1 reply
-
It's not "wrong" - it's just there to force the overload to be used when default
is present. Because it's an intersection, the type constraints for default
in PropOptions<T>
still apply.
Beta Was this translation helpful? Give feedback.
-
Anyone get this working in Storybook 7? Overriding the @vitejs/plugin-vue
in Storybook's config breaks everything
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
2 replies
{{actor}} deleted this content .
-
Anyone get this working in Storybook 7? Overriding the
@vitejs/plugin-vue
in Storybook's config breaks everything
Did you mean that it breaks the app with a postcss unexpected token error?
Beta Was this translation helpful? Give feedback.
-
When adding
vue({
script: {
defineModel: true,
propsDestructure: true,
},
})
to Storybook's viteFinal
config I get the following on boot:[vite] Internal server error: At least one <template> or <script> is required in a single file component.
Beta Was this translation helpful? Give feedback.
-
Nothing much to add to the discussion other than I'm finding defineModel
to be very useful, and am hoping it becomes permanent, not experimental. :)
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
All reactions3 replies
-
I went to use it at my own risk. I hope it becomes official too.
It is constructive when you define a custom component to be used multiple times. It saves much effort and time and prevents redundant code.
Beta Was this translation helpful? Give feedback.
-
I also opened a PR in my company Repo, but we want to merge it if it gets out of experimental
But the PR is already working find 👌
Around 50 files affected
Beta Was this translation helpful? Give feedback.
-
One more shoutout, found this incredibly convenient!
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
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.
You must be logged in to vote
1 reply
-
This doesn't work. Vue wont allow you to bind a value in a v-for.
<SchemaCollectionProvider v-for="schema in schemas" v-model:schema="schema" :key="schema.id">
throws: Internal server error: v-model cannot be used on v-for or v-slot scope variables because they are not writable.
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.
You must be logged in to vote
0 replies
This comment was marked as disruptive content.
-
Jesus Christ, and people complained about Options API having too much Magic. This is straight up confusing as hell. Had to look at the blog post for a better example and I'm still not 100% I understand. Like... creating any variable name with it will make it a prop automatically and also create an
update:variableName
under the hood that is emitted? Is that what's happening? That's so unintuitive. It's the exact opposite of obvious. It should be calledvModelPropEmit
or something to make it more obvious what it's doing.It's marked as experimental. I hope it doesn't actually make it into Vue for real because that is going to make dealing with codebases fucking impossible to understand for people new to Vue. Feels very much like "the bad parts" of JavaScript that no one uses because we all know they are anti-patterns now. Like only people who think they are "clever" would use this, and they would over use and abuse it in awful ways. yikes
LOL emotional baby complaining. You use the words "unintuitive" as to make your point logical and smart and objective but all you're doing is complaining because you don't like something.
Hooks are completely intuitive. They are being used in every language now.
The OptionsAPI was hiding the magic. With Script Setup and its compiler macros, we are in control of that magic.
We use it or we don't. Simply as that. You can always emit all your props changes manually if you want to do more work LOL.
Having a hook that automatically emits props changes for the parent is unintuitive for you? WTF!?
Do you also have issues with .value mutation that automagically triggers a re-render in the view? 😂
I tried to take your comment seriously but you didn't give me anything useful to work with. Using curse words and all, you are just a troll.
Keep using the OptionsAPI there's a "Vue 2 Reviver" now. Haha.
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
So you understand this terrible garbage:
<script setup> const props = defineProps(['modelValue']) const emit = defineEmits(['update:modelValue']) function onInput(e) { emit('update:modelValue', e.target.value) } </script> <template> <input :value="modelValue" @input="onInput" /> </template>
But not this?:
<script setup> const modelValue = defineModel() </script> <template> <input v-model="modelValue" /> </template>
"opposite of obvious", "unintuitive", "the bad parts", "would get abused". Yeah it's very clear you don't actually understand it whatsoever. What is there not to get? It's so easy. It's a two way binding of a value. The model is a reference to the one in the v-model, as if they're the same value. Oh no, props and emits are registered without you knowing, well so do defineProps and defineEmits, better stop using those too.
This function has pretty much the same usage as defineModel, you just need to declare props and emits and pass them through:
const props = defineProps(['modelValue']) const emits = defineEmits(['update:modelValue']) const modelValue = defineModel(props, emits) function defineModel(props, emit, name = 'modelValue'){ return computed({ get: () => props[name], set: value => emits(`update:${name}`, value) }) }
Wow, Composition API so complicated and magic. Only reason it looks dumb is out of my control and now they're fixing it with defineModel. You probably write all those god awful comments saying composition api sucks because you don't understand .value
and this.
is way easier (despite the fact you can just have a reactive variable and call it that
). You probably think mixins are good too (I never want to find out if this is a good insult or not, but I trust Vue in this when they said it was terrible). You could even put this thing in a composable (which are actually good mixins) and have Nuxt auto import it for you. So opaque and unextendable
Wanna know why you don't think it's obvious? Because defineModel is extremely obvious and intuitive and the previous way was extremely unobvious and intuitive, so when you explain how you would do the same previously, as I just did, it looks really stupid. Which is kinda why the new way exists.
New people will find this really easy, because it is, and you can find it easy too if you stop comparing it to the dumbass way of doing it which just makes it seem complicated.
Kinda reminds me of the time where someone said 24 hour time was more complicated than 12 hour time. This has to be satire.
TLDR: It's just a two way binding/reference to a value. It's really, really easy. Composition API based. (and people who use Options API but know it's not as good).
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
Like only people who think they are "clever" would use this
😂 I use this and I don't find myself "clever". I just use the things I find useful. Like, ermmm, everything it's supposed to be in life?
Unless you are the overarchiver that craves for attention and do things in a "special way" than everyone else just to "prove something".
And I'm not of the crew that falls for evey new and shiny thing that comes. I tried the reactivity transform with the $ref
and $$()
and that was unintuitive, and I made a lot of mistakes because I was used to .value. Ultimately I was right, the team saw that people were not liking it and removed it.
defineModel
was the missing piece of the puzzle. There's no utility in having a ref() that automatically forces a render in the view by mutating it, if we can't do the same thing with props that come from the parent. defineModel solves this.
It emits the model change event for us just by mutating the props, and the props keep being reactive.
It doesn't eliminate the need for emits. We still need to emit other events to the parent. It only encapsulates the events to change parent's model values.
I genuinely want to see if you aren't trolling and are just having a bad day. But if I'm wrong, enjoy the attention! :)
And to Vue team:
Great job like always! Wishing you all health, peace, and success.
Beta Was this translation helpful? Give feedback.
-
My concerns are definitely about confusion for newcomers, and overuses too.
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.
You must be logged in to vote
7 replies
-
So are you saying it's just as confusing as props?
No. I'm saying that reusing a naming convention (define...) which is now used for different purposes (not doable otherwise) could be confusing.
I don't get what you mean about the name. You want it to sound more like a core thing
No. That's actually the opposite : It actually sounds like a core thing introducing a new concept, which is not true and this is why I think it could be more easily adopted if it was named as an utility function.
And it's defining a model. toModel makes no sense because it's not converting anything
This. Yes, it's true. I didn't think about it enough to provide something which was perfect, even for me, just to avoid being too verbose in our conversation :).
But actually, provideModel
does make sense.
I hope that you now get the idea of what I mean. If you don't, it's fine. I'm just trying to give my 2 cents about this new feature, nothing more. I don't really understand why you just thumb down everything I say and answer / ask question as if I was trying to destroy a good idea. I'm actually just sharing thoughts but I feel like you don't really try to understand what I say :). Maybe I'm wrong, and then, I'm sorry. But then, I don't really understand where you are trying to bring me...
Beta Was this translation helpful? Give feedback.
-
But it's for the same purposes? It's just writable defineProps. What you're saying is technically true but also not. It's not a core thing, you don't need to have it, but it behaves like a core thing so that doesn't really matter.
And I'm just asking questions, is that not allowed?
Also what about being confusing for newcomers? Is it confusing or not, by which I mean more confusing than, for example, props?
Beta Was this translation helpful? Give feedback.
-
I would only think it's confusing for newcomers in the sense that they may not understand what's it's doing under the hood, so debugging problems with models may be difficult at that point, but the same could also be said of using the v-model directive. If you don't fully read the docs, you may not realize that it's a prop and event handler in one, and it works differently based on whether it's used on a native input or on components. But we're all 100% fine with v-model, and this is maybe only abstracting slightly more than v-model but eliminates a good chunk of common boilerplate.
Beta Was this translation helpful? Give feedback.
-
It's not a core thing, you don't need to have it, but it behaves like a core thing
That's exactly my point. It's not a core thing, but it presents itself as is. Which could sounds confusing.
Is it confusing or not, by which I mean more confusing than, for example, props?
Again, subjectively, I think it's way harder to get (still, for a new dev).
Because no : it's not a writeable props system. It's even a worse definition of what it does internally, as props should never be updated by a child. Don't take me wrong, I keep saying the same stuff again and again : that's fine to have such helpers embedded in vue...
But one simple exemple of why I think it could be confusing for newcommers : defineModel
could allow you to generate a model based on a prop, without having to declare the prop as a model itself. It could make sense to do so, also, I'm not saying you should never do that, but then the concept of what is a model and what isn't could feel blurry if you don't understand that it's a watched ref emitting an event on change.
We could argue that it's easy enough for everybody, but again, I think that initial intuition is better than documentation if it can lead to an almost instant understanding just by being named specifically. Here, reading this name, I wouldn't be surprised if at first, someone instinctively thinks about it as a breaking change in the v-model system as it reuses the same naming convention as core features; which in this case, induces a rework of props/emits system with a third completely different approach serving as your defined "writeable props".
I would only think it's confusing for newcomers in the sense that they may not understand what's it's doing under the hood
Exactly.
But we're all 100% fine with v-model, and this is maybe only abstracting slightly more than v-model but eliminates a good chunk of common boilerplate
Definitely. That's why I'm saying it's a great feature everybody will eventually use. But I think the naming could be improved to have the same feeling we have using useVModel
Beta Was this translation helpful? Give feedback.
-
It behaves like a core thing, so it should have a name like a core thing. defineModel should have always existed, it only behaves like a helper because of the way Vue is architected. If we remade Vue it would be a core thing and probably not be related to emits.
It really is writable props. You said it isn't, but you only know props as read-only, so for you props = read-only props, so when I say models are writable props, you think models are writable read-only props when I mean just props. Read-only props should be read-only, writable props should be writable. "X should be Y" is never a good argument.
I keep hearing this argument that models are somehow unintuitive because it's emitting behind the scenes and for some reason you need to understand that. That's only because defineModel hasn't been here from the start and you're used to it not existing. "A watched ref emitting an event on change" is not simple. Do you know what is? That a model is just a reference to the ref in vmodel. I've only been doing Vue for 8 months, I suppose I could be a newcomer and it's super duper easy. I just looked at a picture comparing defineModel to no defineModel for like 2 seconds and I understood it perfectly. I also have a better explanation here in another reply to someone's comment: #503 (reply in thread)
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
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:
use new method like defineAttribute(), which is sit infront of defineProps() and defineModel(), it will some property is immutable and v-model is mutable. it reduce complexity and in future new comer only need to know defineAttribute()
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
2 replies
{{actor}} deleted this content .
-
The problem is that you are mixing typing and definition of props. It could be possible to implement something like this:
defineProps<{ prop1?: string, modelValue: string }>({ models: [ 'modelValue' ] });
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
May use better keyword:
defineProps<{prop1:string,modelValue:string}>({mutable:['modelValue']})
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
How does defineModel handle v-model modifiers? Are v-model modifiers never used with defineModel?
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
4 replies
{{actor}} deleted this content .
-
You can still access them via the modelModifiers
prop (currently needs to be declared separately):
const props = defineProps<{ modelModifiers: { trim?: true } }>() const msg = defineModel<string>() if (props.modelModifiers.trim) { // ... }
We can potentially make this automatically handled:
const msg = defineModel<string, { trim?: true }>() // ^ Ref<string | undefined> & { modifiers: { trim?: true }} if (msg.modifiers.trim) { // ... }
I think we will probably ship current defineModel
as stable without this though, because:
Beta Was this translation helpful? Give feedback.
-
Follow up on this: we have implemented modifiers support in the latest 3.4.0-rc.3 that doesn't change anything already discussed in this RFC:
const [msg, modifiers] = defineModel<string, 'trim' | 'capitalize'>() if (modifiers.trim) { // ... } if (modifiers.capitalize) { // ... } // type error! if (modifiers.foo) {}
The returned ref implements Symbol.iterator
to support the array destructure.
Also implemented get
& set
transformers so the value conversion can be done without the need for an extra computed wrapper:
const [msg, modifiers] = defineModel<string>({ set(value) { if (modifiers.trim) { value = value.trim() } if (modifiers.capitalize) { value = capitalize(value) } return value } })
Beta Was this translation helpful? Give feedback.
-
@yyx990803 I think it's better to pass the modifiers to the get/set callbacks as second argument:
const msg = defineModel<string>({ set(value, modifiers) { if (modifiers.trim) { value = value.trim() } if (modifiers.capitalize) { value = capitalize(value) } return value } })
Beta Was this translation helpful? Give feedback.
-
While passing modifiers to get
and set
looks more encapsulated, avoids captures, and pollutes the global scope less, there are advantages to having them available globally.
You can change the behavior of your component based on modifiers, or react to the modifiers changing.
For example you couldn't create a .debounced
modifier inside set()
.
Maybe having both (globally available + passed to get/set as second parameter) would be a nice compromise.
Beta Was this translation helpful? Give feedback.
{{actor}} deleted this content .
-
Is it possible to use defineModel inside a component with v-for?
im looping over an array of 'schemas' and these all have their own websocket that updates their own schema, so i would bind it like this
<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.
You must be logged in to vote
1 reply
-
You could do schema, schemaIndex in schemas
and then schemas[schemaIndex]
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.
You must be logged in to vote
1 reply
-
Beta Was this translation helpful? Give feedback.
All reactions-
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.
You must be logged in to vote
0 replies
-
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 local: true
as just being a way to make v-model
optional. If the parent is passing a v-model
value, then it might be reasonable to expect the local
setting to have no impact. In reality, using local mode makes other subtle changes that might be seen as unexpected inconsistencies.
The relevant Vue code that leads to these differences can be seen here:
Problem 1Consider this example:
This uses local: true
, but the v-model
is being passed from the parent anyway. When clicking the button, notice how the parent re-renders twice in the logs. The underlying cause of this double rendering is the internal use of a flush: 'pre'
watcher to emit the event, which runs between the parent renderings.
If you change the example to local: false
it will only render the parent once. The event is now emitted synchronously inside the setter, so the scheduler queue doesn't defer the event.
Perhaps the local watcher should use flush: 'sync'
instead, for consistency with the get
/set
approach?
The same example shows the second problem. This time, take a look at the value for model
that is logged with After update
.
With local: true
, the After update
shows the updated value. With local: false
the logged value is unchanged, as it needs to wait for a parent re-render to update the value.
It could be argued that this is not an inconsistency, it's just what the local
setting does. local: false
stays rigidly in sync with the current prop value, whereas local: true
can get out of sync. However, I think there's potential for confusion if local
is only documented as a way to make v-model
optional, as that alone wouldn't explain the apparent inconsistency in my example. The example is passing a v-model
in both cases, so whether v-model
is optional doesn't really account for the difference.
This is the flip-side of the previous problem.
Consider this example:
The parent attempts to apply upper and lower bounds to the value. With local: false
this works fine, but the child drifts out of sync with local: true
.
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.
You must be logged in to vote
1 reply
-
When trying to address these issues, I realized the previous behavioral difference between local vs. non-local mode is kinda pointless (and in fact, brings about unnecessary confusion).
When local
is false
and the parent doesn't use v-model
on the child, the child ref value will always be undefined
, making it useless. If the user declared the model with required: true
, there would already be a warning about missing modelValue
.
In vuejs/core@f74785b I've removed the local
option, making the model automatically adapt based on whether the parent provides v-model
or not:
v-model
prop, the child ref value will always be in sync with parent prop value.After the commit, Problems (1) and (3) are solved. Behavior difference in (2) still exists, but is no longer determined by the local
option - instead, it depends on whether the parent provided the v-model
props, which I think is reasonable.
Beta Was this translation helpful? Give feedback.
All reactions-
To enable it in quasar, add the following line to quasar.config.js:
build: {
...
viteVuePluginOptions: { script: { defineModel: true } },
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
0 replies
{{actor}} deleted this content .
-
If it's not a ts
project, how could I define the type of the props which is previously defiend by:
props:{ foo: {tyep: Number} }
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
1 reply
-
defineModel('foo', { type: Number })
Beta Was this translation helpful? Give feedback.
-
How do I approach this case where when I emit
the update:modelValue
, I pass multiple arguments?
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:
<select :value="modelValue" @change="$emit('update:modelValue', $event.target.value, modelValue)">
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.
You must be logged in to vote
1 reply
-
When you use the component you can't do that with regular v-model directive, instead you need to handle the event and prop yourself:
<MyComponent :modelValue="modelValue" @update:modelValue="modelValueHandler">
The modelValueHandler function should look like:
function modelValueHandler(newValue, oldValue) { // handle modelValue change }
Beta Was this translation helpful? Give feedback.
-
I'm having an issue with getting the same behaviour that I got with { local: true }
. I assume that the intention is that using { required: false, default: ... }
should yield the same behaviour, but when I try this with a literal object default (default : {}
) or a factory default (default: () => ({})
), I lose all reactivity with anything depending on the model. Inspecting with vue dev tools, I can see that the modelValue is being updated, but nothing depending on it is.
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
0 replies
-
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.
You must be logged in to vote
2 replies
-
Question as someone who likes the 2 way data flow and wants it to be this easy: What is the best practice for this?
I really like mutable props (and basically use defineModel exactly for this).
Beta Was this translation helpful? Give feedback.
-
It makes a lot of sense on dynamic components that take in some sort of an object that you want to edit it with. Like forms and what not. Otherwise it's always felt over-complicated. Just pass in 1 variable that can be edited and be done with it. I don't want to manage all the events and what not.
Beta Was this translation helpful? Give feedback.
-
Using defineModel with an array does not seem to work when splicing.
const model = defineModel({ default: [], type: Array })
model.value.splice(idx, 1) does not update the model.
A workaround seems to be this: model.value = model.value
Is this intended??
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
9 replies
-
<script setup>
const model = defineModel({ type: Array, default: [] })
</script>
<template>
<div>Not updating: {{ model.join(',') }}</div>
<button @click="model.shift();">-1</button>
<button @click="model.push(model.length);">+1</button>
</template>
it like this? SFC Playground
Beta Was this translation helpful? Give feedback.
-
@jods4 default() { return reactive([]) }
works amazing! Thank you so much!
I feel a bit dumb for not trying the default as a reactive([]).
Beta Was this translation helpful? Give feedback.
-
Unfortunately if you set the model.value
in setup or just overwrite model
in the template, it loses reactivity, if you return a reactive like that :/
Beta Was this translation helpful? Give feedback.
-
I have a small issue with the trim Modifier:
on a standard input v-model.trim trims only on blur, however, using defineModel on a child input it will trim on Backspace
<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.
It does this with single spaces as well.
I've tried this in Playground with the same result.
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
1 reply
-
This is the wrong place to discuss issues. Please open a separate issue.
Beta Was this translation helpful? Give feedback.
-
Just upgraded from 3.3, and apparently local
is gone?
Do I understand it right that all defined models are now local by default?
Beta Was this translation helpful? Give feedback.
You must be logged in to vote
1 reply
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emojiYou can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4