0
votes

I have a Vue component with Navigation and layout that uses a bus to communicate with sub components. The bus is passed as a prop to sub components which then use $emit.

export default class Dashboard extends Vue {

  protected bus: Vue = new Vue();

  mounted() {
    if (this.bus != null) {
      this.bus.$on("save", () => {
        //
      });
    }
  }
}

@Component({ props: ["bus"] })
export default class Child extends Vue {
   protected bus!: Vue;

   save() {
      this.bus.$emit("save");
   }  
}

This works fine in the standard case.

In the case of Vue Router how can I inject the bus component from the parent into the children?

1
Params of the router are just key-value pairs, I don't think it is possible to pass object reference there...Alex Brohshtut
Any other idea on how to pass a reference to a child?KingOfCoders
Pass it as a property to the component, or prefix event on the root with something and the pass it in the route props. Like this.$emit(`${this.$route.params.prefix}:save`).Alex Brohshtut
Typically an event bus is not passed around like that. Why not use a global event bus? Further, central state management trivializes component communication, you might consider using Vuex. Save an object in Vuex, load it on any route. Object params can be passed too, but it's probably an anti-pattern.Dan
I use Vuex for application state, but e.g. parent should coordinate displaying error messages from sub components, doesn't feel right to have that in Vuex. Or if the parent shoud fade out if a sub component has an error. Or the parent might need to change other sub components based on one sub component. Using Vuex for this kind of communiction feels not Vue like. I would prefer to go the Vue way with $emit just to child components instead of sub components. I will go with the global bus.KingOfCoders

1 Answers

1
votes

You should create a bus instance in a separate file that you can import into any other file that needs to use it, instance will persist across all places it is imported.

You can use the data() object instead of passing as props as it will act as a global store but that's what Vuex is for.

// BusDepot.js

import Vue from 'vue'

export const DashboardBus = new Vue({
  data: {
   mySharedValue: 'Its global!'
  }
})
import { DashboardBus } from 'BusDepot'

export default class Dashboard extends Vue {

  created () {

    DashboardBus.$on('save', () => {

      console.log(DashboardBus.mySharedValue)

    });

  }

}
import { DashboardBus } from 'BusDepot'

export default class Child extends Vue {

   save() {
      DashboardBus.$emit('save');
   }  
}

If you use Vuex as a central data store to resolve your props pass down issue, you could use the main Vue instance as an Event Bus:

import { DashboardBus } from 'BusDepot'

export default class Dashboard extends Vue {

  created () {
    const vm = this

    vm.$root.$on('dashbaord.save', () => {

      console.log(vm.$store.state.mySharedValue)

    });

  }

}
import { DashboardBus } from 'BusDepot'

export default class Child extends Vue {

   save() {
      this.$root.$emit('dashbaord.save');
   }  
}