Nuxt.js: Using a global event bus

Created on 23 Jul 2017  路  22Comments  路  Source: nuxt/nuxt.js

I want to create a global event bus with Nuxt, (see here). My first instinct was to create a middleware like this:

export default function () {

    let eventBus = new Vue();
    return eventBus; 

}

But I don't have access to the Vue instance in the middleware, I'm not sure where I can put the following code so I can emit and receive events across all pages/components.

I don't really want to import a file into every single component/page that uses the event bus, as it defeats the object of it being globally accessible. Anyone know a way I can achieve this?

Thanks!

help-wanted

Most helpful comment

Hello!
I used the next configuration:

in plugins/eventBus.js

import Vue from 'vue'

const eventBus = {}

eventBus.install = function (Vue) {
  Vue.prototype.$bus = new Vue()
}

Vue.use(eventBus)

in nuxt.config.js (into key "plugins". More info: https://nuxtjs.org/api/configuration-plugins)

module.exports = {
  plugins: [ '~/plugins/eventBus.js' ]
}

in your component

methods:{
   eventhandler(){
       this.$bus.$emit('name-of-event', 'your-data')
  }
}

in your other component

created () {
    this.$bus.$on('name-of-event', (data) { 
      ... 
   } 
}

All 22 comments

You should define it in a plugin. Try this:

    export default function ( {app, store} ){
        app.$bus = new Vue
        if (store) store.$bus = app.$bus
    }

@motia Still getting a "Vue is not defined" error with this code too unfortunately.

@anthonycook is this not better to resolve with Vuex? Just as an idea though as I'm not familiar what you try to archive

@dohomi What I'm trying to do is get my child components to communicate with my parent components/pages. For example when I press a button in child component, the parent component will respond. From what I can gather from Vue's docs, custom events are the best way to do this. However, I can't seem to get them working with Nuxt.

hi, @anthonycook !
Maybe you forgot to import Vue?

import Vue from 'vue'

Hi, @anthonycook, I just tested the event bus and it works fine. Probably the solution is as @KonstantinVlasov suggests.

@KonstantinVlasov as I do similar things like you with a toolbar where users can press SAVE I need to dispatch the click event down to a complete different component. I use a Vuex to toggle a Boolean variable like this:

state.js
export state = {
  toggleSave: false
}

toolbar.js
dispatch('toggleSave', !this.$store.state.toggleSave)

some-random-component.vue
export default{
  watch:{
    '$store.state.toggleSave'(v){
      // do something if toggleSave is changing
     }
  }
}

If you only need a parent child communication and not a global dispatch system you can pass events through your instance via this.$emit(), this.$on() and props. See the doc.

If you're looking for a non parent-child communication, then go with vuex.

I suggest Vuex as well, but that's because I like to have well defined data flow patterns for my projects and not stray from the patterns. IE "we use Vuex to do this, so use Vuex, don't create a one-off event bus between two components", that way it's a bit easier contextually to understand what is going on. Less context switching. And donuts. Lots of donuts.

hello, i'm trying to create a event bus with this plugin:

`import Vue from 'vue'

export default function ({ app, store }) {
app.$bus = new Vue()
if (store) store.$bus = app.$bus
}`

In my nuxt config i have:

plugins: [ { src: '~/plugins/event-bus' } ]

and in a component i'm trying to access the bus with this.$bus but is undefined. in this.$store.$bus is ok.

thank you

Why do you need to register the bus in the store? The store can easily replace a global bus emitter...

@Lacroute is not necessary in the store. i just copy the example of @motia

@javialon26 Ok I see.

The plugin let's you the opportunity to inject a piece of code in the root app. As the doc says, you'll be able to use this piece of code in middlewares or fetch and asyncData hooks.

If you're looking for a global event dispatcher for your components, you should create an external file in assets called bus.js:

import Vue from 'vue'

const Bus = new Vue()

export default Bus

Then in your pages, or component simply import it with import Bus from '~/assets/bus'. Then you'll be able to play with your custom events between your components. Be carefull, you'll bypass lots of good practices with the global bus way.

Again, I suggest you to take a look at vuex, it's not very complicated to understand, and your application will be more structured.

@Lacroute thank you, i understand vuex and actually im using it for auth, locale, menu state, etc. but in some case is more simple just fire an event.

playing with plugins, i found this solution for a global event bus:

`import Vue from 'vue'

const eventBus = {}

eventBus.install = (Vue) => {
Vue.prototype.$bus = new Vue()
}

Vue.use(eventBus)`

with this plugin i have access to this.$bus in all component. what do you think of this approach?

@valdinei use created instead of mounted

Hello!
I used the next configuration:

in plugins/eventBus.js

import Vue from 'vue'

const eventBus = {}

eventBus.install = function (Vue) {
  Vue.prototype.$bus = new Vue()
}

Vue.use(eventBus)

in nuxt.config.js (into key "plugins". More info: https://nuxtjs.org/api/configuration-plugins)

module.exports = {
  plugins: [ '~/plugins/eventBus.js' ]
}

in your component

methods:{
   eventhandler(){
       this.$bus.$emit('name-of-event', 'your-data')
  }
}

in your other component

created () {
    this.$bus.$on('name-of-event', (data) { 
      ... 
   } 
}

@AngelVasquezNep Thanks for the solution !

In the file plugins/eventBus.js you create an a property named install in the bus event object.
Is it recognized somewhere by vue or nuxt ?

@samyfox hi !
The property install is a native method of Vue. It work in Vue and Nuxt.. More info: https://vuejs.org/v2/guide/plugins.html
Have a nice day ! 馃枛馃徑

This approach in Nuxt is causing memory leak, any clue?
If I replace

Vue.prototype.$bus = new Vue()

for

Vue.prototype.$bus = {
  $on: function () {
  },
  $off: function () {
  },
  $emit: function () {
  }
}

The memory leak is gone.
Thanks!

Code more simple, plugin:

import Vue from 'vue';

Vue.prototype.$bus = new Vue();

Use inject nuxt function

Create plugins/bus-inject.js

import Vue from 'vue';

export default (ctx, inject) => {
  const bus = new Vue;
  inject('bus', bus);
};

Register this plugin in nuxt.config.js

plugins: [
    '~/plugins/bus-inject.js',
    //or use this for browser using only
    {src:'~/plugins/bus-inject.js', ssr: false}
]

Now it will be available in components, context and vuex
example-component.vue

export default {
  mounted(){
      this.$bus.$on('messageSent', e => console.log(e));
  },
  asyncData({app}){
     //app.$bus available here also
  }
}

store/index.js

export const state = () => ({
    messages: []
});

export const actions = {
   async sendMessage({commit}){
     const message = (await this.$axios.post('/message', {text: 'hello'})).data;
     commit('pushMessage', message);
     this.$bus.$emit('messageSent');
   }
};

export const mutations = {
   pushMessage(state, message) {
      state.messages.push(message);
      this.$bus.$emit('messagePushed', message);
   }
}

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bimohxh picture bimohxh  路  3Comments

maicong picture maicong  路  3Comments

vadimsg picture vadimsg  路  3Comments

uptownhr picture uptownhr  路  3Comments

bimohxh picture bimohxh  路  3Comments