Custom v-model and .sync patterns
About this series
Communication between components is one of the core parts of frontend frameworks. First, same as React, Vue's data flow is also one-way. You might have come across "two-way data flow using v-model" in some tutorials, but that is not accurate. Data flow in Vue is unidirectional. Vuex is great for sharing data between components, but when it comes to some scenarios such as building a UI library or a state management library, Vuex is not suitable anymore, and we need to utilize the build-in mechanism of Vue itself.
Custom v-model pattern
Here we have two components - Parent
and ChildA
:
Parent
displays a list of desserts which is an object array defined in the data
option, and also passes it to ChildA
's prop value
. It also binds an event named input
onto the ChildA
instance, with the callback function being its parentUpdate
method.
Now let's look at ChildA
:
ChildA
also has a reactive data field named desserts
and its value is from the prop value
that Parent
passes in. At line 29, notice the spread operator - because of reference type, we need to make a copy of the data passed in by props. Otherwise, line 34 will modify the prop array directly, which will break Vue's one-way data flow.
This is how the page looks at the moment:
When the button is clicked, ChildA
will emit the input
event bound onto it by Parent
, and the argument passed to
Parent.parentUpdate()
is ChildA.desserts
. At line 47 in Parent.parentUpdate()
, Parent
updates its desserts
using the value passed from ChildA
. Now the view:
This pattern can make sure some data in Parent
and ChildA
is "in sync". Data still flows one-way as we can clearly see above.
We can also make the code a bit more concise by writing the function inline:
because the logic here is just an assignment statement, we don't really need to define a method, and it works as before. Also, because this type of code is very common in application development, Vue has a shortcut syntax for it: v-model
. If we replace the <ChildA>
tag in Parent
with this one:
We get the exact same result.
In addition, you can change the prop and event name using the model
option https://vuejs.org/v2/api/#model.
.sync pattern
Since the event name is just a string registered in the event system as a key, how about in Parent
we change the event name bound onto ChildA
from input
to the string update:value
, to be more meaningful. Let's go back to the example before using v-model
and update the event name:
As the event name is changed, in ChildA
now it needs to emit the new event name to trigger the event:
Run the code, and it works exactly the same as before. And similar to v-model
, Vue has another syntax shortcut for it which is v-bind:value.sync
. If we update Parent
:
The result is the same.
What's under the hood
When Vue uses its compileToFunctions
function to compile the <template>
of a component:
- for any
v-model
in the<template>
, it will generatev-bind:value="xxx"
andv-on:input="val => this.xxx = val"
. - for any
v-bind:value.sync="xxx"
in the<template>
, it will generatev-bind:value="xxx"
andv-on:update:value="val => this.xxx = val"
. - for any event bound onto a child component tag:
<Child
@anyEvent="parentUpdate"
/>
it will generate an object like so: {anyEvent: parentUpdate}
, and this object will be passed onto the Child component instance (of VueComponent
type). When the Child component gets this object, it will mount it onto itself via this.$on('anyEvent', parentUpdate)
. This way, the Child component can trigger its anyEvent
event via this.emit('anyEvent', args)
( an event name is just a key registered in Vue's event system), and the callback function registered (a method on the Parent component) will be executed.
Conclusion
The custom v-mode pattern and the .sync pattern work well in many cases, but what if we have a grandchild component and a grand grandchild component? Of course, we can repeat these two patterns in each descendant component, but will it start to become a bit clumsy? And this is what the Part 2 of this series is about.
Also check out the rest articles in this Vue.js components patterns series:
Vue.js Components Communication Patterns (without Vuex) - Part 2
Vue.js Components Communication Patterns (without Vuex) - Part 3
Vue.js Components Communication Patterns (without Vuex) - Part 4
Vue.js Components Communication Patterns (without Vuex) - Part 5
Vue.js Components Communication Patterns (without Vuex) - Part 6
Vue.js Components Communication Patterns (without Vuex) - Part 7
Top comments (0)