Sibling Communication in Vue Js Using Event Bus


I wanted to make a single page application using vue. Functionality will be like following, when you click on sidebar link respective component will be load. Like following design I need 4 main component.

  • Main component
  • Sidebar component
  • home component
  • about component
  • contact component
-------------------------------------
home    |     content of respective tab
        |
about   |
        |
contact |
        |
-------------------------------------

In main.vue component, sidebar component and a component from (home.vue, about.vue, contact.vue) will be mount based on sidebar state. This functionality we can achieve through Event bus

following entry js file for calling vue component, instantiate vue for main app and event bus.

entry.js

window.Vue = require('vue');
window.SidebarEvent = new Vue();
const app = new Vue({
    el: '#app',
});

Here I instantiate vue and assign to window.SidebarEvent for using this instance as event bus. And instantiate Vue for main app as well

require all 5 component in entry js file

entry.js

window.Vue = require('vue');
window.SidebarEvent = new Vue();

Vue.component('app-main', require('./components/main.vue'));
Vue.component('app-sidebar', require('./components/sidebar.vue'));
Vue.component('app-home', require('./components/home.vue'));
Vue.component('app-about', require('./components/about.vue'));
Vue.component('app-contact', require('./components/contact.vue'));

const app = new Vue({
    el: '#app',
});

Writing on main.vue component

main.vue html part

<template>
  <transition name="fade">
    <div class='container'>
      <div class='row'>

        <div class='col-md-3'>
          <app-sidebar></app-sidebar>
        </div>

        <div class='col-md-9'>
          <app-home v-if="isSelect('home')" ></app-home>
          <app-about v-if="isSelect('about')" ></app-about>
          <app-contact v-if="isSelect('contact')" ></app-contact>
        </div>

      </div>
    </div>
  </transition>
</template>

main.vue js part

export default {
  mounted() {
    SidebarEvent.$on('sidebarchanged', (data) => {
      this.selectedSidebar = data.selectedSidebar;
    });
  },
  data: function () {
    return {
      selectedSidebar: 'home',
    }
  },
  methods: {
    isSelect(selectedSidebar) {
      return this.selectedSidebar == selectedSidebar;
    }
  }

}

In main component we decided which component is active using isSelect() method inside v-if directive. Based on this we show component. inside data method we return default selectedSidebar is home. In mounted method we are constantly listen to a event called sidebarchanged using SidebarEvent.$on() function. those sidebarchanged event will triggered from sidebar component

sidebar.vue component

sidebar.vue html part

<template>
  <ul>
    <li @click="changeSidebar('home')" :class="{'active': isSelectedSidebar('home')}">Home</li>
    <li @click="changeSidebar('about')" :class="{'active': isSelectedSidebar('about')}">About</li>
    <li @click="changeSidebar('contact')" :class="{'active': isSelectedSidebar('contact')}">Contact</li>
  </ul>
</template>

sidebar.vue js-part

export default {
  data() {
    return {
      selectedSidebar: 'home',
    }
  },
  methods: {
    isSelectedSidebar(selectedSidebar) {
      return selectedSidebar == this.selectedSidebar;
    },
    changeSidebar(selectedGroup, selectedSidebar) {
      this.selectedSidebar = selectedSidebar;
      SidebarEvent.$emit('sidebarchanged', {
        selectedSidebar,
      })
    }
  },
}

In side bar component using changeSidebar, we change sidebar state. Once we click on changeSidebar it will emit ‘sidebarchanged’ event using SidebarEvent.$emit() function. this emit will be receive by Main component SidebarEvent.$on() function. $emit() function will take 2 parameter. first one is event name (in our case sidebarchanged) 2nd one is value we want to pass. In this case we pass value like home about contact to track our state

home.vue, about.vue, contact.vue component

<template>
  <transition name="fade">
    content of home or about or contact
  </transition>
</template>

Transition in vue

Here I have added a transition name with fade. To get those transition we have add following css in our css file

.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}

Thanks

See Also