Vuex or $emit? Basics of communication between Vue components.


Topic: Vuex or $emit? Basics of communication between Vue components.
Published 02.03.2019 Updated 06.03.2019

Introduction

As we develop our Vue App, some features may require communicating back up to the parent component. For example, we may decide to handle Modal events from the main App.vue file than from the file that we've registered it. There are two common methods to achieve this. First is the Vue custom events system and second is Vuex state management pattern. Let's learn some basics!


Creating App with Vue CLI

To begin our tutorial let's create a sample App:

vue create mdb-vuex

This time, instead of the default configuration we choose Manually select features. Then we select babel, eslint (defaults) and of course Vuex from the list. In the next steps we can select the default configuration.

After the initialization of Vue CLI template, we should go to the right dir by typing cd mdb-vuex. 

Futhermore, we need to install mdbvue package:

yarn add mdbvue bootstrap-css-only

Now we're ready to update main.js with necessary styles. Let's add bootstrap-css-only and mdbvue css files:

import Vue from 'vue'
import App from './App.vue'
import store from './store'
import 'bootstrap-css-only/css/bootstrap.min.css';
import 'mdbvue/build/css/mdb.css';

Vue.config.productionTip = false

new Vue({
store,
render: h => h(App)
}).$mount('#app')

There's also the folder structure in the right-aligned image.

As we can see there, most imortant files are located in the src dir:

- App.vue (main app file)
- main.js (for global imports)
- store.js (container that holds your application state)

- /components (dir for new .vue files)


Great! Everything is ready to start coding!



Vue custom events system ($emit)

The most common way to handle events is to pass them as props and then listen to another events by emitting them from the child component's scope. Let's prepare our App for it! 

Firstly we need to create a new component, for example:

EmitModal.vue:

<template>
  <mdb-modal v-if="showModal" @close="closeModal">
    <mdb-modal-header>
      <mdb-modal-title>Modal title</mdb-modal-title>
    </mdb-modal-header>
    <mdb-modal-body class="text-center my-4">Example content</mdb-modal-body>
    <mdb-modal-footer>
      <mdb-btn color="secondary" @click.native="closeModal">Close</mdb-btn>
    </mdb-modal-footer>
  </mdb-modal>
</template>

<script>
import { mdbModal, mdbModalHeader, mdbModalTitle, mdbModalBody, mdbModalFooter, mdbBtn } from 'mdbvue'

export default {
  name: 'EmitModal',
  props: {
    show: Boolean
  },
  components: {
    mdbModal,
    mdbModalHeader,
    mdbModalTitle,
    mdbModalBody,
    mdbModalFooter,
    mdbBtn
  },
  computed: {
    showModal() {
      return this.show
    }
  },
  methods: {
    closeModal() {
      this.$emit('closeModal')
    }
  }
}
</script>

The component is a basic modal. You can find the examples here.

How does it work?

After passing a new show prop value by the parent component, showModal is being recalculated (that's how the computed properties work). 

That is the way to make modal visible. But how to make it disappear, since it was toggled from outside? We just need to use $emit to send a kind of a message back to parent. In this case we will use closeModal event without any parameters.

But what's next? Now we only have to listen to the emitting event inside the parent:

App.vue

<template>
  <div id="app">
    <mdb-btn color="primary" @click.native="toggleEmitModal">Launch Emit modal</mdb-btn>
    <emit-modal :show="showEmitModal" @closeModal="closeModal" />
  </div>
</template>

<script>
import EmitModal from './components/EmitModal.vue'
import { mdbBtn } from 'mdbvue'

export default {
  name: 'app',
  components: {
    EmitModal,
    mdbBtn
  },
  data() {
    return {
      showEmitModal: false
    }
  },
  methods: {
    toggleEmitModal() {
      this.showEmitModal = !this.showEmitModal
    },
    closeModal() {
      this.showEmitModal = false
    }
  }
}
</script>

<style>
#app {
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

As we can see, inside the parent scope there is @closeModal="closeModal" event, which simply changes this.showEmitModal value to false. 

Perfect! Now let's achieve the same functionality using Vuex


Vuex

At the center of Vuex application is the store. A "store" is basically a container that holds your application state globally. Let's add a new store variable inside store.js file:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    showVuexModal: false
  },
  mutations: {
  },
  actions: {
  }
})
Then we can create a new component with an access to $store:

VuexModal.vue

<template>
  <mdb-modal v-if="showModal" @close="closeModal">
    <mdb-modal-header>
      <mdb-modal-title>Modal title</mdb-modal-title>
    </mdb-modal-header>
    <mdb-modal-body class="text-center my-4">Example content</mdb-modal-body>
    <mdb-modal-footer>
      <mdb-btn color="secondary" @click.native="closeModal">Close</mdb-btn>
    </mdb-modal-footer>
  </mdb-modal>
</template>

<script>
import { mdbModal, mdbModalHeader, mdbModalTitle, mdbModalBody, mdbModalFooter, mdbBtn } from 'mdbvue'

export default {
  name: 'VuexModal',
  components: {
    mdbModal,
    mdbModalHeader,
    mdbModalTitle,
    mdbModalBody,
    mdbModalFooter,
    mdbBtn
  },
  computed: {
    showModal() {
      return this.$store.state.showVuexModal
    }
  },
  methods: {
    closeModal() {
      this.$store.state.showVuexModal = false
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

This time there is no need to $emit any event, as you can see above. We just refer to the global Vuex store.

Let's now update the parent component:

App.vue

<template>
  <div id="app">
    <mdb-btn color="primary" @click.native="toggleEmitModal">Launch Emit modal</mdb-btn>
    <mdb-btn color="secondary" @click.native="toggleVuexModal">Launch Vuex modal</mdb-btn>
    <emit-modal :show="showEmitModal" @closeModal="closeModal" />
    <vuex-modal />
  </div>
</template>

<script>
import EmitModal from './components/EmitModal.vue'
import VuexModal from './components/VuexModal.vue'
import { mdbBtn } from 'mdbvue'

export default {
  name: 'app',
  components: {
    EmitModal,
    VuexModal,
    mdbBtn
  },
  data() {
    return {
      showEmitModal: false
    }
  },
  computed: {
    showVuexModal() {
      return this.$store.state.showVuexModal
    }
  },
  methods: {
    toggleEmitModal() {
      this.showEmitModal = !this.showEmitModal
    },
    toggleVuexModal() {
      this.$store.state.showVuexModal = !this.$store.state.showVuexModal
    },
    closeModal() {
      this.showEmitModal = false
    }
  }
}
</script>

<style>
#app {
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Great! It's working without sending props or emitting events!

Summary

In this tutorial, you learned basics of the communication between components in common Vue App and in Vue app with the Vuex state management pattern. I hope it will be helpful while developing your complex Vue Applications!

Write
Please insert min. 20 characters.
Details of the article
  • Category: Vue
  • Specification: MDB Vue 5.1.0 + Vuex 3.0.1