Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at how to handle events in Vue components, including how to use v-model
with custom components and passing data between parent and child.
Listening to Child Components Events
We can send events from a child component to the parent to run code in the parent component.
For example, if we want to add a button to each post to change the font of the page, we can do that as follows:
src/index.js
:
Vue.component("post", {
props: ["post"],
template: `
<div>
<h2>{{post.title}}</h2>
<div>{{post.content}}</div>
<button v-on:click="$emit('change-font')">
Change Font
</button>
</div>
`
});
new Vue({
el: "#app",
data: {
posts: [{ title: "Foo", content: "foo" }, { title: "Bar", content: "bar" }],
font: ""
},
methods: {
changeFont() {
this.font = this.font === "" ? "courier" : "";
}
}
});
We added the changeFont
method to let us change the font in the Vue app. This will be called when the change-font
event is emitted from the post
component.
The change-font
event is emitted from post
when the Change Font button is clicked.
The event is emitted with the $emit
method. The string we passed in is the name of the event.
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<div v-bind:style="{'font-family': font}">
<post
v-for="post in posts"
v-bind:post="post"
v-on:change-font="changeFont"
></post>
</div>
</div> <script src="src/index.js"></script>
</body>
</html>
In the template, we have the v-bind:style
attribute to set the font of the div
dynamically with v-bind:style=”{‘font-family’: font}”
.
When the change-font
event is received, then the changeFont
method will be called to toggle the font between Courier and the default.
The $emit
method also takes a second argument with the value that we want to send back to the parent.
For example, we can rewrite the font change code as follows:
src/index.js
:
Vue.component("post", {
props: ["post"],
template: `
<div>
<h2>{{post.title}}</h2>
<div>{{post.content}}</div>
<button v-on:click="$emit('change-font', 'courier')">
Change Font
</button>
</div>
`
});
new Vue({
el: "#app",
data: {
posts: [{ title: "Foo", content: "foo" }, { title: "Bar", content: "bar" }],
font: ""
},
methods: {
changeFont(font) {
this.font = this.font === font ? "" : font;
}
}
});
In the code above, we passed in 'courier'
to the $emit
function. The item in the second argument will be available as the $event
in the parent component.
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<div v-bind:style="{'font-family': font}">
<post
v-for="post in posts"
v-bind:post="post"
v-on:change-font="changeFont($event)"
></post>
</div>
</div> <script src="src/index.js"></script>
</body>
</html>
In the template, we pass in $template
, which contains 'courier'
to the changeFont
method.
The changeFont
method toggles the font between the font
that we passed in as the argument and an empty string.
So it does the same thing as before.
This lets us pass data from a child component to a parent component.
Using v-model on Components
v-model
is the same as the v-bind:value
and v-on:input
together. This means that:
<input v-model="text">
is the same as:
<input v-bind:value="text" v-on:input="text= $event.target.value" >
Since components take props and emit events, we can combine the value
propr and the input
event binding into the v-model
directive.
For example, we can use it to make our own custom input as follows:
src/index.js
:
Vue.component("styled-input", {
props: ["value"],
template: `
<input
v-bind:style="{'font-family':'courier'}"
v-bind:value="value"
v-on:input="$emit('input', $event.target.value)"
>
`
});
new Vue({
el: "#app",
data: {
text: ""
}
});
In the code above, we created the style-input
component to change the font of the input to Courier.
Also, we use v-bind:value
to get the value of the value
prop and emit the input
event as the text is inputted into the input box.
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head> <body>
<div id="app">
<styled-input v-model="text"></styled-input>
<br />
{{text}}
</div> <script src="src/index.js"></script>
</body>
</html>
In the code above, we can use v-model
to bind to the text
data field since v-model
is the same as combining v-bind:value
with v-on:input
.
styled-input
emitted the input
event and takes the value
prop, so we can merge them together into v-model
.
Conclusion
We can emit events with the $emit
from a child component to the parent. It takes 2 arguments. The first argument is the string for the event name, and the second argument is the object that we want to pass to the parent.
The parent can access the object passed from the child by listening to the event with the v-on
directive and then retrieve them item pass from the child with the $event
object.
v-bind:value
and v-on:input
is the same as v-model
, so v-bind:value
and v-on:input
can be combined into one and v-model
can be used with custom components.
This lets us create custom inputs easily.