Dynamically listen to custom event on parent component - vue.js

In my custom library I have several components which run ajax requests, and when a given request fails the component emits an error event.
Then, in my main component I want to listen to all error events emmited and run the method handleErrors, but for that I have to go on every component and add #error="handleErrors".
Is there a way to configure my main component to catch error events dynamically and call handleErrors without going on each component and adding it? Preferrably changes to the main component only.

You can use the EventBus system in Vue instance. Actually EventBus is a different Vue instance your Main Vue instance. You make your own Event bus system.$emit, $on and $off events.
event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
and now you are ready to use.
some-component.vue
// Import the EventBus.
import { EventBus } from './event-bus.js';
// Send the event on a channel (eventName) with a payload (the click count.)
EventBus.$emit('eventName', this.clickCount);
other-component.vue
// Import the EventBus.
import { EventBus } from './event-bus.js';
// Listen for the eventName event and its payload.
EventBus.$on('eventName', clickCount => {
console.log(clickCount)
});
// Stop listening.
EventBus.$off('eventName');
more information and example
https://alligator.io/vuejs/global-event-bus/

Figured a way to do it without refactoring the individual components: just injected the listener in every component using a mixin.
The result:
// Before starting up the main instance:
Vue.mixin({
created: function() {
this.$on('error', function(error) {
// Then I handled the errors here.
);
}
});

Related

Using the mitt-library in Vue with Webpack hot-reload causes problems?

In my Vue3 app, I'm using the mitt eventbus library to emit and receive events between components.
I put this in onMounted of a list component that needs to refresh:
mitt.on("list_refresh", (evt) => {
refresh();
});
In another component that contains the list-component as a child (or grandchild), I do this in a method:
mitt.emit("list_refresh", {});
This works ok, but while developing with hot-reload on, the events seem to be emitted multiple times, as if they're created extra each time the app reloads, instead of overwriting the old ones.
When I reload the entire page in the browser, it works fine again.
Any idea to prevent this?
It looks like your component is missing a corresponding off() call to remove the event listener. During hot reload, the current component instances unmount, and new ones mount; so if you're not removing current event listeners, you'll just pile on new event listeners. To resolve the issue, use the onUnmounted hook to remove the event listener when the component is removed from the DOM.
Also, make sure to pass cached function references (instead of inline functions) to mitt.on() and mitt.off() to ensure the given event listener lookup succeeds in mitt.off():
// mitt.on('list_refresh', () => refresh()) ❌
mitt.on('list_refresh', refresh) ✅
mitt.off('list_refresh', refresh)
Your setup() should look similar to this:
import { onMounted, onUnmounted } from 'vue'
export default {
setup() {
const refresh = () => { /*...*/ }
onMounted(() => mitt.on('list_refresh', refresh))
onUnmounted(() => mitt.off('list_refresh', refresh)) 👈
}
}

Whats wrong with my eventBus implementation?

I want to trigger a function from another component which is not parents and child relationship. i try to use eventbus, but i am not sure am i implemented it correctly, since the function is not working. i have created a boot file eventBus.js and added it into the quasar config, but its still not working. Anyone can help me to check which part did i implemented wrongly?
eventBus.js
import Vue from 'vue'
const eventBus = new Vue();
export default eventBus
Vue.prototype.$eventBus = eventBus;
below is the function that i want to get from this page, (this.getSchedule(), it is working perfectly on current page).
mounted(){
this.$eventBus.$on("refreshListSchedule", ()=>{
scheduleDate = this.scheduleDate
console.log(scheduleDate)
console.log("Testing successed")
this.getSchedule()
})
},
The page that i want to trigger it
this.$eventBus.$emit('refreshListSchedule')
It is better if you manually import from eventBus.js in each component where you want to emit or subscribe for events - don't rely on Vue.prototype.
And don't forget to unsubscribe in the beforeDestroy hook.

Mounting component instance to $root from plugin

I'm creating a plugin, where there will be a single instance of ModalsContainer per $root.
Because of that - I want to create the instance manually, rather than having people add <v-modalscontainer/> to their app.
Code below does just that, it loads components and registers them for Vue to use, then creates an instance of the container and mounts it - the issue is, it's mounted nowhere - the method mounted() of component has console.log() in it and clearly it gets executed without an issue - but it's not mounted in any of $roots.
import Modal from './components/Modal.vue'
import ModalContainer from './components/ModalsContainer.vue'
const plugin =
{
install(Vue, options = {})
{
Vue.component('v-modal', Modal)
Vue.component('v-modalscontainer', ModalContainer)
var instance = new (Vue.extend(ModalContainer))
instance.$mount()
}
}
export default plugin

Vue.js: Global event bus is failing to catch the event

I am trying to create a global event bus to be emitted from Source.vue to be caught in Destination.vue. The latter component has to run a function when the event is caught.
Source.vue:
.then(() => {
eventBus.$emit("fireMethod");
})
Destination.vue:
updated() {
eventBus.$on("fireMethod", () => {
this.reqMethod();
});
}
main.js:
export const eventBus = new Vue();
I have also imported the main.js file in both of the components. But that doesn't seem to work as the event is not caught at all. What's wrong here?
Thanks in advance
The updated hook runs after data changes on your component and the DOM re-renders. Hence on the initial render your event listener is not registered as the event is not triggered at that time.
Kindly go through this fiddle to get a clear idea about vue components life cycle.
Hence in Destination.vue:
Your event hook should be mounted and not updated.
mounted() {
eventBus.$on("fireMethod", () => {
this.reqMethod();
});
}
To register global event bus you can use this in your main.js
Vue.prototype.$eventHub = new Vue(); //global event bus
And then in your components use it like
//on emit
this.$eventHub.$on('your-event',function(){});
//to emit
this.$eventHub.$emit('your-event',data);

How to trigger js-vue-modal within actions

I'm using the vue-js-modal library. I also use vuex. How do I trigger open a modal within actions? (as this.$modals.show('modal-name') only works within a Vue component)
The plugin adds $modals to the Vue class prototype, so all you would need to do is import Vue in your store and then emit the event to show the model. For example:
// store.js
import Vue from 'vue'
const vm = new Vue()
...
actions: {
openModal: (context, params) => {
// do things
vm.$modals.show('my-modal')
// do stuff
}
}