Is there a way to be notified when event listeners are added to a Vue component? - vue.js

I have a custom Vue component that does not render to HTML.
When the component is first mounted, I am able to loop through this.$listeners and optimize the underlying non-HTML event implementation accordingly (e.g. not to emit mousemove type events unless something is listening).
To complete this process I'd like to know when listeners are programatically added later through $on().
Is there any non-polling way to be notified of this? My current workaround is to listen for and emit everything but that is a poor solution.
Note that in some instances I want to be able to use the events in question, and others not. For example:
<custom-component #eventThatWouldFireOften="doSomething" #anotherEvent="doMoreStuff"/>
and another usage might be simply.
<custom-component ref="custom"/>
...
mounted() {
this.$refs.custom.$on('anotherEvent', ...)
}
So in the first case the result would be:
CustomComponent tells the underlying API it wants to listen for eventThatWouldFireOften and anotherEvent
CustomComponent receives eventThatWouldFireOften and anotherEvent events from the underlying API and re-emits them as Vue events that can be listened to using v-on or # syntax.
..and the second case the result would be:
CustomComponent tells underlying API it doesn't want to listen for anything just yet
When the parent of CustomComponent is mounted it programatically listens for anotherEvent. That needs to be communicated down to the base API (what I'm trying to solve).

Related

How can I remove manually added event listeners on beforeDestory hook of one component before redirecting to any another component, in Vuejs2?

I want to know the concept of the below thing-
I have created one component and set up its respected event listeners. Now, I want to remove those listeners on this component's beforeDestroy hook before redirecting to another route that will create another component.
but what I noticed is, beforeDestory hook of the first component is calling even after the second component's created hook.
I want to destroy the first component completely and then create another component.
// To set up the event listeners
created() {
this.EventBus.$on('myCustomEvent', payload => {
// some code here
)}
}
// To destroy the event listeners
beforeDestroy() {
this.EventBus.$off('myCustomEvent');
}
Any suggestions?
In search of an answer to your question, I came to the conclusion that it is better to refuse to use EventBus altogether.
Here is some information on this and some thoughts from there:
I have that feeling that having an EventBus in Vue is an anti-pattern, especially if you’re using VueX but I can’t quite put my finger on it. At the point you want to be sharing data like that, wouldn’t it be better to use a store to handle all of those “events” / “mutations”?
Also, looking at the solution to this issue, you are doing everything right and there is no other way.

Try to understand Vue2's $emit

I wonder how Vue2's $emit works?
On its API(https://v2.vuejs.org/v2/api/#vm-emit), it says:
Trigger an event on the current instance. Any additional arguments
will be passed into the listener’s callback function.
If current instance means the component defined, while its main usage is to send signal to parent(https://v2.vuejs.org/v2/guide/components.html#Sending-Messages-to-Parents-with-Events)
I wonder how to understand this(the way how the event passed is pretty confused to me)?
THanks
The wording on the docs might create confusion to some people.
Maybe something in the lines of: "Makes the vm current instance dispatch an event" would be clearer?
In the end it is just a classic pub/sub pattern: your component instance emits / dispatches / fires an event, and other components (typically the parent one) listen / subscribe (v-on / #) to that event.
Simply put, $emit register an event in a component, and then you can listen to this event in other places wherever the component is used.
Say if you have a Child.vue and somewhere in this component, you did:
.$emit('some-event')
Then you can listen to this event when Child component is reused, for instance in another component SomeComponent.vue, you can do:
<template>
<child #some-event="doSomething"></child>
</template>
So here the event is triggered in the child component, but you decide what to do in the parent component with doSomething. Hope this makes sense!

How to communicate between components within a certain component?

Suppose I have a tree of component like this:
<widget>
<widget-header>
<panel-toggle></panel-toggle>
</widget-header>
<widget-body>
<panel></panel>
</widget-body>
</widget>
Now supposed I want the panel-toggle component to be able to toggle the visibility of the panel component. I could have it affect a prop passed down from widget through to each component, but that didn't seem like the best solution. I tried sending an event with this.$emit(eventName) but the event is only picked up by the immediate parent of the element emitting the event. In this case, that would be panel-toggle emitting the event and only widget-header being able to pick it up. I tried sending the event across the root element with this.$root.$emit(eventName) and picking it up with this.$root.$on(eventName), but then it is picked up by all widget components and that is no good. What I ended up doing is sending the event with this.$parent.$parent.$emit(eventName) and then picking it up from panel with this.$parent.$parent.$on(eventName). While that worked, it doesn't seem like the right way to go about this.
What would be the correct way to achieve this communication between components within the component widget only with Vue? Is the answer somehow related to the ref feature?
Since you're concerned (and with good reason) about the globalness of a global event bus, the solution is a localized event bus. Create a data item in the parent:
panelBus: new Vue()
and pass it to each of the children as a prop. Now they have a private communcation channel for just the two of them.

is there A Communication Manager for vue components?

I was Vue.js now for a project and created and used a lot of components.
Now I started to have the problem of having too many eventemitters and props that I need to keep track of.
I guess to illustrate the problem the best I will use an example:
Lets say you have a main.vue and 2 or 3 Components.
One contains a button that should manipulate the other 2 components or switch them out.
Now I need to emit an event to the main.vue and then main.vue has to change a binded variable and pass props down to the other 2 components.
Alright: Now lets put the button in a component of of a component. You need to make sure that every link between a parent and a child is correct.
Now create a bit project and put a button in another components and you have to change everything.
So is there a good way to avoid this?
Something like a broadcast function so that every component is receiving the event?
Or a Manager that is handling the communication of all components?
use a flux pattern (vuex)
At first you may think that this does not really answer the question, since it deals with storage of data, and not handling of events. The flux pattern changes the architecture of your application by creating a single store (think database) that all components can read and write from. Coupled with the reactive nature of the reactive frameworks such as vue (or react), the components will react to a change in data. So instead of tightly coupling component A to D through B and C, you'd have component A listen to mutations in object X, and component D makes changes to object X. When the change happens, component A gets updated without having to listen to any of the children's $emit functions firing. At first it may seem daunting, but the investment is worthwhile.

Invoke method in child component when the component is changed dynamically from the parent component

I have a simple component hierarchy, in which there is one parent component, which contains the following template <div><component :is="current"></component></div> and two child components, which are dynamically 'switched' out, via the value of current.
The logic for the 'switching' of components is being handled in the parent component. However, when I try to execute a method in the second child component (in response to an event emitted from the first child component, being listened to in the parent component, and altering the value of current in the parent component) through the mounted lifecycle hook, the expected result is not observed.
Essentially, I cannot find a decent way of invoking a child component's methods when that component is 'switched' in as the current component in the parent component.
I have realistically looked at using the $refs and $childrenproperties on the instance, but these do not seem to be of any assistance. I have also reasoned using the event system. I usually 'define' an event listener in the mounted lifecycle hook, however, I refer to the 'issue' of using the lifecycle hooks
What is the usual approach to this situation?
There are two options that immediately come to mind for child components invoking methods on being the "current" child component:
1) Using the mounted lifecycle hook. In order for this to work, you must be using v-if in order to conditionally display the child component, otherwise the child component will not trigger the mounted lifecycle hook. Otherwise, if you're using v-show or some other mechanism for conditionally displaying the child component, the mounted hook will only ever trigger once.
2) Using watched properties. In lieu of the above, you could do something like the following:
<child-component :target_prop="current"></child-component>
Vue.component('child-component', {
. . .,
props: ['target_prop'],
watch: {
target_prop: function() {
if(this.target_prop == your_expected_value) {
//execute the desired method for this component
}
}
}
});
This is, of course, just a proof-of-concept example and there are plenty of alternative ways to use the watched properties to your advantage.
I've also heard of using a "bus" to handle inter-component communication, but I don't believe it would be any better than the above two solutions (and is arguably worse).
Personally, I prefer the mounted lifecycle hook approach, though there's the caveat that any "settings" or data in your child components will be lost on switching between them (although you could also emit a settings object on destruction and store/restore those settings as needed, but that could get messy). You'll have to make the final judgment on which approach better suits your needs and which consequences you're more comfortable dealing with.