Topic: Vuex or $emit? Basics of communication between Vue components.
Mikołaj Smoleński
staff posted 2 years ago
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: {
}
})
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>
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>
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!
- Category: Vue
- Specification: MDB Vue 5.1.0 + Vuex 3.0.1
Thank you for this Tutorial!
Works perfect for me! Only the line
@click.native="closeModal"in theVuexModal.vueworks, when i remove the.nativefrom the@click-event.Vuex is very comfortable to use and feels a little faster than
$emit.