This content originally appeared on DEV Community and was authored by Alex Grozav
While writing my Vue.js UI Library, Inkline, I had to find a way to make some components work both with and without providing a model value (v-model
). While it's not a common scenario, it's something that you'll definitely come across if you're writing a library and you're serious about Developer Experience (DX).
I call them Optionally Controlled Components, because they're supposed to work out of the box without providing a v-model
, but will give you complete control over their state if you do provide a v-model
.
The Menu Example
One prime example of an Optionally Controlled Component would be a menu that can be opened (expanded) or closed (collapsed). Let's call the component simply MyMenu
.
From a Developer Experience perspective, you'll probably want your library user to be able to drop a <my-menu>
into their code and start adding collapsible content right away, without having to worry about handling its open or closed state.
Here's what the component would look like without v-model
support:
<template>
<div class="my-menu">
<button @click="toggleMenu">
Menu
</button>
<menu v-show="open">
<slot />
</menu>
</div>
</template>
<script>
export default {
name: 'MyMenu',
data() {
return {
open: false
};
},
methods: {
toggleMenu() {
this.open = !this.open;
}
}
}
</script>
The Optional Model Value
So far so good. Let's consider the following scenario: your user wants to be able to open or close the menu from somewhere else. We know we can open and close the menu internally at this point, but how do we allow the library user to optionally control the state?
There's a future-proof solution I found, that will save you a lot of trouble. Here's what it looks like:
<template>
<div class="my-menu">
<button @click="toggleMenu">
Menu
</button>
<menu v-show="open">
<slot />
</menu>
</div>
</template>
<script>
export default {
name: 'MyMenu',
emits: [
'update:modelValue'
],
props: {
modelValue: {
type: Boolean,
default: false
}
},
data() {
return {
open: this.modelValue
};
},
methods: {
toggleMenu() {
this.open = !this.open;
this.$emit('update:modelValue', this.open);
}
},
watch: {
modelValue(value) {
this.open = value;
}
}
}
</script>
Try a basic example out live on CodeSandbox.
You can see above that I've added the usual modelValue
prop to provide v-model
support in Vue 3, but mainly I've done three things:
- I'm setting the initial value of our internal
open
state property to be equal to the one provided viav-model
. This works wonders, because when there's nov-model
it would be equal to the specified default,false
in our case. - I'm emitting an
update:modelValue
event every time I change the value ofthis.open
internally - I've added a watcher that ensures I'm always keeping the internal
open
value in sync with the incoming externalmodelValue
prop.
Conclusion
Awesome, isn't it? It's important to never forget about Developer Experience. Something as small as this can add up to precious hours of saved development time if done correctly and consistently.
I hope you learned something interesting today. I'd love to hear how the Optionally Controlled Components pattern helped you out, so feel free to reach out to me. Happy coding!
P.S. Have you heard that Inkline 3 is coming with Vue 3 support? Read more about it on GitHub.
This content originally appeared on DEV Community and was authored by Alex Grozav
Alex Grozav | Sciencx (2021-03-18T12:11:15+00:00) Making v-model Model Value Optional in Vue.js. Retrieved from https://www.scien.cx/2021/03/18/making-v-model-model-value-optional-in-vue-js/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.