DEV Community

Cover image for Modifying component data with event emitters in Vue.js
Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

Modifying component data with event emitters in Vue.js

Written by Nwose Lotanna✏️

This post will introduce you to how data and its state can be passed from a child component to its parent component in Vue.js using event emitters.

Before you start…

This post is suited for developers of all stages, including beginners. Here are a few things you should already have before going through this article:

  • Node.js version 10.x and above installed. You can verify whether you do by running the command below in your terminal/command prompt:
node -v
Enter fullscreen mode Exit fullscreen mode
  • A code editor — I highly recommend Visual Studio Code
  • Vue’s latest version, installed globally on your machine
  • Vue CLI 3.0 installed on your machine. To do this, uninstall the old CLI version first:
npm uninstall -g vue-cli
Enter fullscreen mode Exit fullscreen mode

Then, install the new one:

npm install -g @vue/cli
Enter fullscreen mode Exit fullscreen mode
  • Download a Vue starter project here
  • Unzip the downloaded project
  • Navigate into the unzipped file and run the command to keep all the dependencies up to date:
npm install
Enter fullscreen mode Exit fullscreen mode

Passing data through components

To pass data values from parent components (like the app.vue) to child components (like nested components) inside the app component, Vue.js provides us with a platform called props. Props can be called custom attributes you can register on a component that let you define data in the parent component, give it a value, and then pass the value to a prop attribute that can then be referenced down in the child components.

This post will show you the reverse of this process. To pass down and update data values in a parent component from the child component in such a way that all other nested components will be updated, too, we use the emit construct to handle event emission and updating of data.

LogRocket Free Trial Banner

Demo

You will be taken through the process of emitting events from a child component, setting up listening on the parent component in order to pass data from the child component, and then finally updating the data value.

If you followed this post from the start, you will have downloaded and opened the starter project in VS Code. The project is the finished, complete code to this post here.

The reason behind having that as a starter project is so that you can play around with the props concept before getting introduced to reversing the process.

Getting started

In the folder, you will find two child components: test.vue and test2.vue, with the parent component being the app.vue file. We will use the headers of the two child components to illustrate this event emission approach. Your Test.vue file should look like this:

<template>
  <div>
    <h1>Vue Top 20 Artists</h1>
    <ul>
      <li v-for="(artist, x) in artists" :key="x">
        <h3>{{artist.name}}</h3>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Test',
  props: {
    artists: {
      type: Array
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
li{
    height: 40px;
    width: 100%;
    padding: 15px;
    border: 1px solid saddlebrown;
    display: flex;
    justify-content: center;
    align-items: center;
  }
a {
  color: #42b983;
}
</style>
Enter fullscreen mode Exit fullscreen mode

To make the header receive the title from an implicit definition in the data property section, you create the data section and add the definition, and then add the interpolation symbol in the template, like this:

<template>
  <div>
    <h1>{{header}}</h1>
    <ul>
      <li v-for="(artist, x) in artists" :key="x">
        <h3>{{artist.name}}</h3>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Test',
  props: {
    artists: {
      type: Array
    }
  },
  data() {
    return {
     header: 'Vue Top Artists'
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

If you run the application, you get exactly the same interface you got right at the start. The next step is to change this defined property on click.

Toggling the header

To toggle the header, you will have to add an event listener on click to the header and specify the function that will contain the logic that should happen when it is clicked.

<template>
  <div>
    <h1 v-on:click="callingFunction">{{header}}</h1>
    <ul>
      <li v-for="(artist, x) in artists" :key="x">
        <h3>{{artist.name}}</h3>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Test',
  props: {
    artists: {
      type: Array
    }
  },
  data() {
    return {
     header: 'Vue Top Artists'
    }
  },
  methods: {
    callingFunction(){
      this.header = "You clicked on header 1";
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Now your header changes to the string inside the calling function on click.

Toggling The Header

Setting the emitter

At this stage, you want to pass on this same behavior to the parent component so that on click, every title nested in the parent component changes.

To do this, you will create an emitter that will emit an event in the child component that the parent component can listen to and react (this is just the same as event listener logic for components).

Change the script section in the Test.vue file to the code block below:

<script>
export default {
  name: 'Test',
  props: {
    artists: {
      type: Array
    },
    header: {
      type: String
    }
  },
  data() {
    return {
      // header: 'Vue Top Artists'
    }
  },
  methods: {
    callingFunction(){
      // this.header = "You clicked on header 1"
      this.$emit('toggle', 'You clicked header 1');
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Here, the type of data expected of the header was defined as a prop. Then, in the method, there is an emit statement that tells Vue to emit an event (just like any other — e.g., a click event) on toggle and pass the string as an argument. This is all you need to set up an event that will be listened to inside another component.

Listening to the emitted event

Now the next thing to do after an event is created is to listen and respond to it. Copy this code block into your app.vue file:

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <Test v-bind:header="header" v-on:toggle="toggleHeader($event)" />
    <Test v-bind:artists="artists" />
    <test2 v-bind:header="header"/>
    <test2 v-bind:artists="artists" />
  </div> 
</template>
<script>
import Test from './components/Test.vue'
import Test2 from './components/Test2'
export default {
  name: 'app',
  components: {
    Test, Test2
  },
  data (){
    return {
      artists: [
       {name: 'Davido', genre: 'afrobeats', country: 'Nigeria'},
       {name: 'Burna Boy', genre: 'afrobeats', country: 'Nigeria'},
       {name: 'AKA', genre: 'hiphop', country: 'South-Africa'}
      ],
      header: 'Vue Top Artists'
    }
  },
  methods: {
    toggleHeader(x){
      this.header = x;
    }
  }
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
Enter fullscreen mode Exit fullscreen mode

In the template section, you can see that the first component, Test, has two Vue directives on it. The first is the v-bind, which binds the initial header property to the implicit definition in the data object under the artists array; on initialization, the string Vue Top Artists is shown.

The second directive is the v-on, which is for listening to events; the event to listen to is toggle (remember, you already defined it in the Test component), and the calling function on it is the toggleHeader. This function is created, and the string from the child component is passed through the $event argument to display here.

The implication

This passes data through the emitter to the parent component, and so because other components are nested in the parent component, the data in every one of the nested components re-renders and updates. Go into the test2.vue file and copy this code block into it:

<template>
  <div>
    <h1>{{header}}</h1>
    <ul>
      <li v-for="(artist, x) in artists" :key="x">
      <h3>{{artist.name}} from {{artist.country}}</h3>
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'Test2',
  props: {
    artists: {
      type: Array
    },
    header: {
      type: String
    }
  }
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
li{
    height: 40px;
    width: 100%;
    padding: 15px;
    border: 1px solid saddlebrown;
    display: flex;
    justify-content: center;
    align-items: center;
  }
a {
  color: #42b983;
}
</style>
Enter fullscreen mode Exit fullscreen mode

Here, the data interpolation was set and specified to be a string in the props object. Run the application in your development server:

npm run serve
Enter fullscreen mode Exit fullscreen mode

Updating The Headers

You see that once the event is reacted to in the parent component, all the components updated their header even though the definition was specified in just one child component.

You can find the complete code for this tutorial here on GitHub.

Conclusion

You can see another interesting side of using events in Vue with emitters: you can now create an event in one component and listen to and also react to it in another component. This can have many use cases that will be really beneficial in your workflow — happy hacking!


Editor's note: Seeing something wrong with this post? You can find the correct version here.

Plug: LogRocket, a DVR for web apps

 
LogRocket Dashboard Free Trial Banner
 
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
Try it for free.


The post Modifying component data with event emitters in Vue.js appeared first on LogRocket Blog.

Top comments (0)