"Exploring the Power of Vue 3 Event Bus Through the Composition API

I recently set up mitt and I'm facing difficulties dispatching events to another component. The issue arises due to the absence of this in the setup() method, making it challenging to access the app instance.

Here's my current approach:

import App from './App.vue'
const el = document.getElementById('app')

import mitt from 'mitt';
const emitter = mitt();

const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount(el);

In the desired component, I aim to dispatch an event

export default {
   setup() {
      function toggleSidebar() {
          this.emitter.emit('toggle-sidebar');

          console.log(this); // binds to setup(), not the vue instance.
      }
   }
}

Due to the absence of this, accessing .emitter is not possible. What am I overlooking? How can I effectively utilize mitt in Vue 3 Composition API?


Interestingly, using v2 syntax allows me to access this.emitter. However, I'm keen on exploring the Composition API method.

export default {
  mounted() {
    console.log(this.emitter); // works
  }
} 

Answer №1

If you want to implement an event bus in Vue 3 Composition API, the process involves using Vue 3's latest provide API in your main.js file and then utilizing inject in any component:

1. Start by installing mitt:

npm install mitt

2. Providing the Event Bus:

Within main.js

import { createApp } from 'vue';
import App from './App.vue';

import mitt from 'mitt';                  // Import mitt
const emitter = mitt();                   // Initialize mitt

const app = createApp(App);
app.provide('emitter', emitter);          // ✅ Provided as `emitter`
app.mount('#app');

3. Injecting the Event Bus

3a. In Any Component - Emitting an event

import { inject } from 'vue'

export default {
  setup() {
    const emitter = inject('emitter'); // Inject `emitter`
    const mymethod = () => {
      emitter.emit('myevent', 100);
    };
    return {
      mymethod
    }
  }
}

You can call mymethod from a button click or similar events.

3b. In any Component - Listening for the event

import { inject } from 'vue'

export default {
  setup() {
    const emitter = inject('emitter');   // Inject `emitter`

    emitter.on('myevent', (value) => {   // *Listen* for event
      console.log('Received myevent!', `Value: ${value}`);
    });
  },
}

Console Output

Received myevent! Value: 100 

Answer №2

If you need to access the global property component, you can utilize the getCurrentInstance method:

Here is an example in JavaScript:

import { getCurrentInstance } from 'vue';
export default {
  setup() {
    // Retrieve current instance
    const internalInstance = getCurrentInstance(); 
    // Obtain emitter from the instance
    const emitter = internalInstance.appContext.config.globalProperties.emitter;
  }
} 

Answer №3

Up to this point, I've implemented the following code to provide access to the "emitter".

//main.ts
import mitt from 'mitt'
const emitter = mitt()
export default emitter

Within the components, I subsequently utilize

import emitter from '@/main';

So far, this approach has proven effective in both Vue2 and Vue3 - particularly with the options API.

However, I have encountered challenges when it comes to the new vite server and hot module reload (hmr). Is there a more optimal way to structure this?

Answer №4

To implement Event Bus with Composition API, follow these steps:

Start by installing and registering mitt in your app.js similar to how it is done below:

import App from './App.vue'
const el = document.getElementById('app')

import mitt from 'mitt';
const emitter = mitt();

const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount(el);

Next, organize the code for accessing the global emitter into a separate file named accessEmitter.js within the js/Composables folder:

import { getCurrentInstance } from 'vue';

export default function accessEmitter() {
    return getCurrentInstance().appContext.config.globalProperties.emitter;
}

Now you can use this functionality in your components:

<script setup>
import accessEmitter from '@/Composables/accessEmitter.js'
const emitter = accessEmitter();

// register listener
emitter.on("myEvent", (data) => {
    console.log('my event');
});
   
// remove listener
emitter.off("myEvent");

</script>

Answer №5

While @Dan's solution is effective, it may result in a 'emitter' is of type 'unknown' error in typescript. @acantepie has provided a solution for this issue which can be found here. The solution involves using a singleton class like so:

import mitt from "mitt";

export default mitt()

To use it:

import EventBus from "../lib/EventBus";
...

EventBus.emit(...)
EventBus.on(...)
...

Hopefully this will benefit someone in need like myself :)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the near method to Parse.Query a class

I'm currently working on my first iOS application, which allows users to add annotations to a map for others to view. In this project, I have decided to utilize Parse as the backend service. What I already have: There is a class called "Pins" in Par ...

Calculating the 3D Longitude and Latitude coordinates in a THREE.js globe using the radius along with the x and y values

Currently, I am exploring a Codepen where JSON data is being utilized to feed into a JavaScript application that maps coordinates using x and y values. Rather than utilizing longitude and latitude to map a location like Hong Kong, the developer uses these ...

Using jqGrid to load additional JSON data after the initial local data has already been populated in the

I encountered a specific issue: I have a compact form with four choices. Users can choose to fill them out or not, and upon clicking 'Ok', a jqGrid is loaded with data based on those selections. To accommodate dynamic column formatting, my servle ...

Achieving success in element-ui vuejs involves validating the state with the :error parameter

I recently developed an app using Laravel, Vuejs, and Element-ui. Within my form, I have utilized the "error" property to indicate that Laravel validation is in place. <el-form-item label="First Name" prop="firstname" :error="registerForm.errors.get(&a ...

Using backslashes to escape JSON values within a value in Angular

When retrieving JSON data from the backend, I often encounter an issue where the value is set to "key": "\$hello" and it results in an "Unexpected token d". Is there a way in Angular to handle or escape these characters once received from the server? ...

Implementing html5mode in Express.js and Angular.js for cleaner URLs

I've been working on resolving the issue of avoiding # in my Angular app with an ExpressJS server-side setup. I found a solution to enable html5mode and it worked well. However, whenever there is another 'get' request to fetch data from a di ...

Prevent individual elements from shifting around on browser resizing within a React form

Having issues with a React form that includes an image gallery and input fields. import React, { Component } from 'react'; import ImageGallery from 'react-image-gallery'; import { Container, Row, Col, InputGroup, Button, FormControl, ...

Attempting to bind an input parameter using NgStyle in Angular version 2 and above

Issue: I am in need of a single component (spacer) with a width of 100% and a customizable height that can be specified wherever it is used in the HTML (specifically in home.html for testing): number 1 <spacer height="'200px'"></spa ...

Leverage Vue3's v-html feature without the need for additional wrapping tags by using script

Is it possible to use Vue's v-html directive within a Vue 3 <script setup> setup without needing an additional wrapping tag? I am looking to achieve something similar to the following: <script setup> const html = ref(`<pre></pre& ...

How can the hreflang tag be used correctly in a React application that supports multiple languages?

I have a React application with 3 pages(routes) and I support 2 languages (English and Spanish). Should I insert the following code into the <head></head> section of the public\index.html file like this? <link rel="alternate" ...

Running a JavaScript file from Docker to fill a MongoDB database is not working when using the WSL shell on Windows 10

I'm facing some issues while trying to populate my MongoDB using a script. Every time I run the script, I encounter errors even though the Docker container is up and running. For reference, I'm on Windows 10 using WSL shell. https://i.stack.img ...

Exploring the history and present state of Vue lifecycle hooks

Is there a way to access previous and current data in the updated lifecycle hook in Vue, similar to React? I want to be able to scroll a list of elements to the very bottom, but for this I need: The already rendered updated DOM (to calculate the scroll) ...

Struggling with linking my Angular Controller with my View and experiencing difficulty establishing a connection

I'm encountering an issue while attempting to link a controller to my view. The error I keep receiving is as follows: Error: ng:areq Bad Argument Argument 'TestAppCtrl' isn't a function, received undefined Here's the content ...

How can I stretch a background image using jquery to cover the entire document instead of just the window

I have implemented a plugin called https://github.com/srobbin/jquery-backstretch to effectively stretch the background image. The problem arises when the content of the page is long enough to create a scrollbar. In this case, while scrolling, the backgrou ...

Trigger JavaScript code following a specific occurrence

Struggling to find a solution due to my limited knowledge in JS, I've decided to pose the query myself: How can I trigger my JavaScript after a specific event? With a form in place, I aim for the JS to execute once the final radio button is selected b ...

Retrieve the value of [routerLinkActive] in the component's class

Recently, I've been working on a tab component called TabComponent and it includes the following HTML template: <a [routerLink]='link' [routerLinkActive]="[is-active]">link label</a> <button>Close tab</button> The c ...

Extract data from JSON object on the server side

Within my web service, I have a method that returns a JSON object: {name:'xx'} When making an Ajax request and parsing the response with 'eval', I encountered an issue. onComplete:function(req){ var data=eval(req.responseText); / ...

Storing information in Firebase using React.js

When storing an object in Firebase, I expected the structure to be as shown in the image below. However, what I received was a generated running number as a key. This is the code I used to store the object in Firebase: var location = []; location.push({ ...

javascript ondrag while self-pressing

Within this div, I have a series of p elements. My goal is to drag the selected element into the input field. Here's an example: var input = document.getElementById("test"); input.addEventListener('drop', function (event) { event.pr ...

How to harness the power of loops in JavaScript

Having trouble getting this code to repeat a CSS transition properly. for (var i=0 ; i<4 ; i++){ setTimeout(function() { $('#top-left').css('margin', '45px 0 0 45px'); $('#top-mid' ...