DEV Community

Cover image for Sharing Data Between Components in Vue.js
Alex Mourer
Alex Mourer

Posted on

Sharing Data Between Components in Vue.js

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
working-example

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.

file structure

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.

Oldest comments (9)

Collapse
 
dysiode profile image
Patrick Kennedy

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?

Collapse
 
alexmourer profile image
Alex Mourer

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.

Collapse
 
ronokdev profile image
Farhan fuad Ronok

Very good post!!
Keep it up. 👍👍👍👍👍👍

Collapse
 
giorgosk profile image
Giorgos Kontopoulos 👀 • Edited

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 ?

Collapse
 
alexmourer profile image
Alex Mourer

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 file event-bus.js is not a huge lift, it is an additional item the component is dependent on.

I think your solution is 100% acceptable.

Collapse
 
franciscosuca profile image
franciscosuca

This was really helpful for a beginner like me, thanks!

Collapse
 
fperniavp profile image
Fabián Pernía

Clear and simple. Thanks!

Collapse
 
kimmydoray profile image
Kimmydoray

Nice Sir Alex, what a great tutorial thanks. :) <3

Collapse
 
franciscosuca profile image
franciscosuca

Great explanation! thank for your content!