{{announcement.body}}
{{announcement.title}}

Type-Based Global Events in Vue.js

DZone 's Guide to

Type-Based Global Events in Vue.js

In this article, we discuss how to create type-based global events in Vue.js to better manage issues commonly seen in dynamically-typed languages.

· Web Dev Zone ·
Free Resource

In one of the latest freelance projects of mine, my client prefers Vue.js, which is recently super popular on the frontend side. So, I dove into Vue. I can say that it is very practical and effective. Besides, when we compare it with other predominant competitors like Angular and Aurelia, we can easily notice Vue has a very small learning curve.

However, it didn't take long for me to have a feeling that my code was getting unmanageable. This wasn't a big surprise to me because this is often the trade-off with dynamically-typed languages.

Today, I am going to show an effective way of using global events in Vue.

A Simple Event Bus in Vue

The typical way of implementing a global event bus in Vue is just using the Vue object itself:

JavaScript




xxxxxxxxxx
1


1
// create a Vue instance somewhere you can access globally
2
let eventBus = new Vue()
3
 
4
// register an event handler
5
eventBus.$on("onAppStarted", () => console.log("App Started!"))
6
 
7
// publish an event
8
eventBus.$emit("onAppStarted")



Super easy, right?

However, there are a couple of problems hidden under this cool dynamic syntax.
You may also like: How and Why We Moved to Vue.js.

The Problem Coming From Strings

As long as our application has more than a couple of lines, sooner or later, we start stressing to follow which components publish and which others listen to them.

Therefore, we can imagine how hard it is to identify a simple typo in a string-based event name, especially in a large project:

JavaScript




x


 
1
eventBus.$on("onApStarted", () => {
2
  // notice the typo in event name, it should be "onAppStarted"  
3
})



Implicit Event Parameters

This is another problem we should notice because we don't define any corresponding type or interface for our event — only God knows what and how many parameters might be in our event.

In order to identify them, we have to do these kinds of tests:

JavaScript




xxxxxxxxxx
1


 
1
eventBus.$on("onAppStarted", (...args) => {
2
  args.forEach(e => console.log(e))    
3
})



A Proper Solution Comes From ES6+

As a fan of the statically-typed Java world, I prefer using types clearly unless it's super unconventional for the specific language. Thus, I will show a solution to get rid of these string-based event names by using the capabilities, which ECMAScript 6 and later offers.

Defining Event Types

Let's create a separate file to define our event types:

JavaScript




xxxxxxxxxx
1
28


1
/**
2
* Event type to publish when app loads
3
* ProducedBy: components/preload.js
4
* ConsumedBy: App.vue, views/MainPanel.vue
5
**/
6
export class AppStartEvent {
7
 
8
  constructor(){
9
    // An event type with no arguments
10
  }
11
 
12
}
13
 
14
/**
15
* Event type to publish when code changes
16
* ProducedBy: views/CodePanel.vue
17
* ConsumedBy: views/MainPanel.vue
18
* @param {object} editor   editor instance
19
* @param {string} code     changed code value inside the editor
20
**/
21
export class CodeChangeEvent {
22
 
23
  constructor(editor, code){
24
    this.editor = editor
25
    this.code = code
26
  }
27
 
28
}



As we notice, defining event type classes and parameters explicitly in constructor offers us great readability.

Although it is optional, we recommend keeping comments updated. This provides a way to follow the components, which deal with a certain event type.

Importing Event Types

To use our type-based events, we can easily import them into our components:

JavaScript




xxxxxxxxxx
1


 
1
import {AppStartEvent, CodeChangeEvent} from "@/app-events"



As we specify explicitly every event type we need, it brings us another important benefit that we can easily identify what events are involved in a component.

Registering an Event

In order to register our event, we simply use our event types and their static name properties:

JavaScript




xxxxxxxxxx
1


 
1
import {AppStartEvent} from "@/app-events"
2
 
3
eventBus.$on(AppStartEvent.name, () => console.log("App Started!"))



Plus, we can expect the event type instance itself as a single argument instead of more than one arguments:

JavaScript




xxxxxxxxxx
1
10


 
1
import {AppStartEvent, CodeChangeEvent} from "@/app-events"
2
 
3
// we can access the event type instance as a single argument
4
eventBus.$on(AppStartEvent.name, event => console.log(event))
5
 
6
// also can access to event parameters
7
eventBus.$on(CodeChangeEvent.name, event => {
8
  console.log(event.editor)  
9
  console.log(event.code)  
10
})



Publishing an Event

Now, we can publish our events simply by creating a new instance of that event type:

JavaScript




xxxxxxxxxx
1


1
// no parameters
2
eventBus.$emit(AppStartEvent.name, new AppStartEvent())
3
 
4
// with parameters
5
eventBus.$emit(CodeChangeEvent.name, new CodeChangeEvent(editor, "some code here..."))



Implementing a Wrapper Class

Certainly, we may proceed to define a class, as EventBus and wrap the basic methods of Vue instance:

JavaScript




xxxxxxxxxx
1
13


1
class EventBus {
2
 
3
  $eventbus = new Vue()
4
 
5
  listen (eventClass, handler) {
6
    this.$eventBus.$on(eventClass.name, handler)
7
  }
8
 
9
  publish (event) {
10
    this.$eventBus.$emit(event.constructor.name, event)
11
  }
12
 
13
}



Therefore, we can use it in a more practical way:

JavaScript




xxxxxxxxxx
1


1
// register an event handler
2
EventBus.listen(AppStartEvent, () => console.log("App Started!"))
3
 
4
// publish an event
5
EventBus.publish(new AppStartEvent())



Using as a Plugin

In addition, we may prefer to use our EventBus as a Vue Plugin:

JavaScript




xxxxxxxxxx
1
33


1
export default {
2
 
3
  $eventBus: null,
4
 
5
  install (Vue, options) {
6
    this.$eventBus = new Vue()
7
  },
8
 
9
  listen (eventClass, handler) {
10
    this.$eventBus.$on(eventClass.name, handler)
11
  },
12
 
13
  listenOnce (eventClass, handler) {
14
    this.$eventBus.$once(eventClass.name, handler)
15
  },
16
 
17
  remove (eventClass, handler) {
18
    if (handler) {
19
      this.$eventBus.$off(eventClass.name, handler)
20
    } else {
21
      this.$eventBus.$off(eventClass.name)
22
    }
23
  },
24
 
25
  removeAll () {
26
    this.$eventBus.$off()
27
  },
28
 
29
  publish (event) {
30
    this.$eventBus.$emit(event.constructor.name, event)
31
  }
32
 
33
}



Certainly, to be able to use the plugin, we should import and register it to our Vue instance:

JavaScript




xxxxxxxxxx
1


1
import EventBus from '@/plugin/vue-event-bus'
2
 
3
Vue.use(EventBus)



Consequently, we can simply import and use our EventBus in any other Vue component as well:

JavaScript




xxxxxxxxxx
1


 
1
import EventBus from '@/plugin/vue-event-bus'
2
import {AppStartEvent} from "@/app-events"
3
 
4
// register an event handler
5
EventBus.listen(AppStartEvent, () => console.log("App Started!"))
6
 
7
// publish an event
8
EventBus.publish(new AppStartEvent())



Finally

In this short tutorial, I have explained how to implement type-based global events and use them in Vue.

You can find the code of the sample plugin over on GitHub.


Further Reading

Topics:
javascript ,vue framework ,web dev ,vue ,react ,angular ,spa ,tutorial

Published at DZone with permission of Yavuz Tas . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}