I am trying to create a global event bus so that two sibling components can communicate with each other. I have searched around; however, I cannot find any examples of how t
As you write ES5 JavaScript you have to be aware of the fact that what you refer to by using the this
keyword might change, according to the scope, it is called from.
A useful metaphor to get your head around the this
concept is to think of the curly braces in ES5 as fences, that contain/bind its own this
.
When you use this
in the callback function of your event bus, this
does not refer to your Vue component, but the bus object, which has no count data, so the data you expect to update doesn't.
If you have/want to write ES5 syntax a common workaround (besides binding this
as suggested by the accepted answer) is to assign the this
keyword to a variable like so:
created: function(){
var self = this;
bus.$on('inc', function(num){
alert(num)
self.count = num;
});
}
If you can write ES6, do so whenever possible. You can always compile/transpile down to ES5 with Babel. The accepted answer shows you how by using arrow functions.
Arrow functions work in that case because they do not bind their own this
.
To stick with the fence metaphor: imagine the ES6 arrow poking a hole in your function fence, so the outer this
can pass through and you can call this
as intended.
To learn more about ES6 arrow functions visit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
How about this? Assume Vue.js 2.
Create a reusable Event-Bus component and attach it to Vue
via plugin pattern:
// ./components/EventBus.vue
import Vue from 'vue'
export const EventBus = new Vue()
// ./plugins/EventBus.js
export default {
install(Vue) {
const { EventBus } = require('../components/EventBus')
Vue.prototype.$bus = EventBus
}
}
// ./main.js
import EventBus from './plugins/EventBus'
Vue.use(EventBus)
Then, you can do anywhere in your code:
this.$bus.$emit('some-event', payload)
As a side note, try to utilize the Event-Bus pattern as last resort.
The problem is that within your bus.$on
function, this
refers to the bus. You just need to bind the current Vue instance to that function using .bind()
:
bus.$on('inc', function(num){
alert(num)
this.count = num;
}.bind(this));
You should also check out https://github.com/vuejs/vuex if you want to manage global application states.
EDIT: Since this page seems to get a lot of clicks I want to edit and add another method, per ChristopheMarois in the comments:
EDIT: In effort to make this answer a little clearer, and so future readers don't need to read comments here's what's happening:
Using a fat arrow like below binds the lexical scope of 'this' to the component instead of to the event bus.
bus.$on('inc', (num) => {
alert(num);
this.count = num;
});
Or removing the alert:
bus.$on('inc', (num) => this.count = num);
This is answered long back, here is my solution using in vue.js-2
main.js
import Vue from 'vue'
import App from './App'
export const eventBus = new Vue({
methods:{
counter(num) {
this.$emit('addNum', num);
}
}
});
new Vue({
el: '#app',
template: '<App/>',
components: { App }
});
comp1.vue
//Calling my named export
import { eventBus } from '../../main'
<template>
<div>
<h1>{{ count }}</h1>
<button @click="counterFn">Counter</button>
</div>
</template>
<script>
import { eventBus } from '../../main'
export default {
name: 'comp-one',
data() {
return {
count: 0
}
},
methods: {
counterFn() {
eventBus.counter(this.count);
}
},
created() {
eventBus.$on('addNum', () => {
this.count++;
})
}
}
</script>