In this tutorial, we'll explore generating data in one component, and passing it to another in Vue.js.
TL;DR
Here is an example of a working CodeSandbox
https://codesandbox.io/s/l4yj6jxpqz
Why would we need to do this?
If you've worked on a large-scale Vue application, chances are, at some point you've wanted to generate data in one component and then do something with it in a separate component. In this tutorial, we will pass user inputted text from one component and display it as a list in another. We will not be using any dependencies outside of Vue.js.
How do we do this?
First, we'll need to have Vue.js up and running. This is covered here, or you can set up a CodeSandbox.
If you used the Vue CLI or CodeSandbox to set up your app, you'll likely already have the initial structure of your project in place. In case you don't, we'll need a place to display our future components.
// App.vue file
<template>
<div id="app">
</div>
</template>
<script>
export default {
name: "App"
}
</script>
The above code is a good starting point for this tutorial. In the provided CodeSandbox example, this file is named App.vue
. The code shown here will serve as the foundation for displaying the components we will build.
Next, we'll start building our components.
In the example listed, there have been two components created in a folder named components
.
The first file we'll look at is UsrMsg.vue.
// UsrMsg.vue file
<template>
<div>
<input
placeholder="Enter Text Here"
v-model="tempMessage"
@keyup.enter="submit"
/>
</div>
</template>
<script>
export default {
name: "UsrMsg",
data() {
return {
tempMessage: ""
};
},
methods: {
submit: function() {
this.$emit("inputData", this.tempMessage);
this.tempMessage = "";
}
}
};
</script>
In this file, you'll see an input
element. The input
element updates the data field tempMessage
by using Vue's v-model
.
<input
placeholder="Enter Text Here"
v-model="tempMessage"
@keyup.enter="submit"
/>
Upon hitting enter
, the method submit
is called. Inside this method, we use $emit
(emit: "to send out a beam") to make tempMessage
available to a parent component. $emit
takes a string as the first parameter, in this case, inputData
. It can also accept a second parameter, which is usually a piece of data. In our example, we are passing the value of tempMessage
. This could also be an integer, a variable, an array, or an object. You'll also notice that we set tempMessage = ""
after the $emit
. This clears out the input field to accept the next value.
submit: function() {
this.$emit("inputData", this.tempMessage);
this.tempMessage = "";
}
Let's add the UsrMsg
component to our App.vue
file.
// App.vue file
<template>
<div id="app">
<UsrMsg @inputData="updateMessage" />
</div>
</template>
<script>
import UsrMsg from "./components/UsrMsg";
export default {
name: "App",
components: {
UsrMsg
},
data: function() {
return {
childData: ""
};
},
methods: {
updateMessage(variable) {
this.childData= variable;
}
}
};
</script>
First, we want to make the data that we are emitting from the component available. We do that by adding a listener around the data we are emitting. We chose inputData
as the data name in our component, so this will be our listener. The second piece executes a method called updateMessage
.
<UsrMsg @inputData="updateMessage" />
Whenever the data inside our component changes, the updateMessage
method will be executed. Inside this method, we make the component data available through the data field childData
. It's now ready to be passed as a prop to another component.
updateMessage(variable) {
this.childData= variable;
}
Now, let's build the component we will be passing the data to.
We'll start by creating a file named Results.vue.
// Results.vue file
<template>
<div>
<li v-for="(message, index) in messageList" :item="message" :key="index">
{{ message }}
</li>
</div>
</template>
<script>
export default {
name: "Results",
props: {
msg: {
type: String
}
},
data: function() {
return {
messageList: []
};
},
watch: {
msg: function() {
this.messageList.push(this.msg);
}
}
};
</script>
In this example, we are creating a list based off an array called messageList
. It is not required to push the data into an array but it is a bit more exciting for demo purposes.😜
<li v-for="(message, index) in messageList" :item="message" :key="index">
{{ message }}
</li>
The messageList
array is keying off the data prop msg
.
props: {
msg: {
type: String
}
}
As data is passed to this component, through the data prop msg
, it is pushed to the messageList
array. We use the watch
property to add on to the array upon new data in the msg
prop.
watch: {
msg: function() {
this.messageList.push(this.msg);
}
}
Our <li>
element will update when new items are added to the messageList
array.
Now, we can add the Results component to our App.vue
file.
<template>
<div id="app">
<UsrMsg @inputData="updateMessage" /> <Results :msg="childData" />
</div>
</template>
<script>
import UsrMsg from "./components/UsrMsg";
import Results from "./components/Results";
export default {
name: "App",
components: {
UsrMsg,
Results
},
data: function() {
return {
childData: ""
};
},
methods: {
updateMessage(variable) {
this.childData = variable;
}
}
};
</script>
We add in our component Results
and pass in childData
through the prop msg
.
<Results :msg="childData" />
We can now generate data in the UsrMsg
component and pass it through to the Results
component.
Top comments (9)
Nice writeup Alex, thanks for sharing.
I have forked your example and have made it work with a Global EventBus using EventBus.$emit and EventBus.$on. This way any Component can choose to listen to this event which makes for a cleaner solution I think.
You can see my changes here codesandbox.io/s/mq2yl9lw8p
Do you know why would someone choose one over the other method ?
Hey George! Your fork looks great, thanks for providing this.
I did look into using
EventBus.$emit
for the example, but I was trying to keep the components as modular as possible. While creating the fileevent-bus.js
is not a huge lift, it is an additional item the component is dependent on.I think your solution is 100% acceptable.
This was really helpful for a beginner like me, thanks!
Very good post!!
Keep it up. 👍👍👍👍👍👍
This approach to watching the property is very unique. Is there a usecase where it would be preferable to storing the message list in the parent or emitting events from the parent and listening in the child?
Hey Patrick, you are correct. The
watch
property section in the example is somewhat unique. I wanted this component to be able to accept a string from any source and then create an array from that inside the component.I would be interested in seeing an example of what you are suggesting. If you have time, feel free to fork the codesandbox example.
Clear and simple. Thanks!
Nice Sir Alex, what a great tutorial thanks. :) <3
Great explanation! thank for your content!