[Vuejs]-Dynamic component event bus not work in vue?

2đź‘Ť

This problem is a good demonstration of the downsides of the event bus pattern:

You, the developer, have to carefully ensure that sender and receiver of all events actually exist at the same moment in time.

In your scenario, that’s not the case:

  • When Test1Component emits the event, Test2Component doesn’t exist yet.
  • After Test2Component has been created by HomeComponent just a moment later, the event is already “gone”.

My usual disclaimer as a Vue core team member: Don’t use the Event bus pattern.

👤Linus Borg

2đź‘Ť

Event Bus is just fine but problem is in implementation.

You are very correct in case of you un-comment code In home component, I tested on your snippet it emit only once event bus test [console.log]

With events you need to take care this

  1. You need to define listener. [ make sure you define them before emitting event ]
  2. Now you just emit event. [ to work we need listener to listen first]

In your case you wrote listener function $on in created event of theTest2Component

Now just think for now, You don’t have any listeners at beginning as in home compo you just commented that listener code .[ its initial step ]

Now when you click on click me button you are changing component to new component[Test2Component], It is mounted and its created event will fire then this listener will start listening to event

but you missed this

this.$emit("changeComponent"); // this is first [ fires/emit ]
this.$bus.$emit("test"); // THEN THIS EMIT

So, When it just start changing component from compo1 tocompo2testfired/emitted directly without wait, its notsynchronousit isAsynchronousIt will not wait to finish all stuff ofchangeComponent. It will be emitted immediately [test`]

Guess what Now when test emits, at that time Dom operation to add component, ITS NOT DONE YET, and test is emitted

So, No listeners are there, so no console.log

But if you see if, you UNCOMMENT listener in home function listener is well defined before emit of test event so it output in console.

I hope you understand it, if not let me know point I will explain it in details

Another thins is that you added $bus in prototype so all components has its own bus. instead you can use GLOBAL event Bus

as in example you can see.

in es6 you can do

//bus.js
const bus = new Vue({});
export default bus;

to import it in other components

import bus from './bus.js';

// ... do bus.$on ..
// ... do bus.$emit ..    
var $bus = new Vue({});

Vue.component('Test1', {
	template: `
		<div class="blog-post">
			<h3>test1</h3>
      <button @click="changeComponent">click me</button>
		</div>
	`,
  methods: {
    changeComponent() {
      $bus.$emit("chng");
      $bus.$emit("test");
    }
  }
});

Vue.component('Test2', {
	template: `
		<div class="blog-post">
			<h3>test2</h3>
		</div>
	`,
  created() {
    $bus.$on("test", () => console.log("test in test2 will not fire as we are little late to listen it"));
  }
});


new Vue({
	el: '#app',
	
	created: function(){
	 console.log('created');
   $bus.$on("chng", () => this.changeComponent());
   $bus.$on("test", () => console.log("test in test2 main/HOME"));
	},
  data() {
    return {
      currentComponent: "Test1"
    };
  },
  methods: {
    changeComponent() {
      this.currentComponent = "Test2";
    }
  }
});
<!DOCTYPE html>
<html>
<head>
	<script> console.info = function(){} </script>
	<script src="https://code.jquery.com/jquery.min.js"></script>
	<script src="https://vuejs.org/js/vue.js"></script>
	<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width">
	<title>Stack Overflow - Hardik Satasiya</title>
	<style>.half{width:50%;float:left;}</style>
</head>
<body>
	<div id="app">
		<component :is="currentComponent" @changeComponent="changeComponent"></component>
	</div>
</body>
</html>

0đź‘Ť

Your test is not correct.

Basically, Test2Component is not created in the time Test1Component emit this.$bus.$emit("test");

You saw 2 console.log because codesandbox use hot reload modules, so this.$bus.$on("test", () => console.log("test in test2")); is still registered if when you change your code.

If you unregister when component destroyed in Test2Component, test in test2 will never be logged

Test2Component.vue

<template>
  <div>
    <h1>test2</h1>
    <button @click="changeComponent">click me</button>
  </div>
</template>

<script>
export default {
  created() {
    console.log("Component 2 is created");
    this.$bus.$on("test", this.testLog);
  },
  beforeDestroy() {
    this.$bus.$off("test", this.testLog);
  },
  methods: {
    testLog() {
      console.log("test in test2");
    },
    changeComponent() {
      this.$emit("changeComponent");
    }
  }
};
</script>
<style>

</style>

Demo: https://codesandbox.io/s/2485jw460y

If you comment

  beforeDestroy() {
    this.$bus.$off("test", this.testLog);
  },

in component 2, you will see test in test2 is printed many times after few clicks

(if you change the code, please refresh to run from beginning)

👤ittus

Leave a comment