Vue EventBus & the Lifecycle - javascript

I have a question about how to use an event in one component to trigger an event in another. I have two components, both child components at the same level. If the updated() method is called in component1.vue, I want component2.vue method setValues() to fire.
This is what I have within created on component2.vue:
created() {
EventBus.$on('updatedComponent', () => {
this.setValues();
});
}
And this is within the updated() method in component1.vue:
EventBus.$emit('updatedComponent');
I have eventbus imported already in both components, and event bus does work (I tested with a console log within the $on) but it still does not work. Do I need to be placing my EventBus.$on in a different lifecycle hook?

Your problem seems to be related to not starting the updated(), You need to update the view to trigger the updated()

Related

component supposed to listen to emitted events from child

I've starting exercising vuejs and I've learned that to communicate data from child component back to parent component we use this.$root.$emit('name-of-event', myobject);
which would be received from parent with the help of this.$root.$on('name-of-event');
In the other hand, I got a vuejs project which I use to compare what I had learned with what is implemented in it and there I found that the component listening to my event is not the parent of that component (the tag of the component triggering the event is not rendered in the one who is listening to it)
My question: is it always the case that the direct parent is the one who listens to the triggered event ? could other component be listening to emitted events ?
myAcomponent.vue :
updateDate(value) {
//body of updateDate method
this.$root.$emit('date-updated', this.project);
}
myBcomponent.vue :
<script>
created() {
this.$root.$on('date-updated', project => {
this.updateproject(project);
});
}
</script>
<template>
//no call in template for myAcomponent
</template>
Is it always the case that the direct parent is the one who listens to the triggered event ? - Answer is Partially Yes, There are two scenarios or use cases :
If you are working on a large application and there is a need to share data between multiple components and these components don't have any relation with each other, then you have to use a state management solution. You can use Pinia - which is the state management library recommended by the Vue core team.
If you want to share the data only between the siblings under single parent, In that case you can give a try to this solution.
Pass data from one child to parent component using event emitter.
Capture the event in parent and then pass back to another child by using props.
there is a better way to improve the event emit
lets say you have component A and B you want to transfer data from A to B also they don't have direct relationship
1.create JavaScript file and add the below code on JavaScript file save it FIleName.js
import Vue from 'vue';
export const EventBus = new Vue();
2 on component A.vue emit the event on methods or on api call
import {EventBus} from "path/FIleName.js";
update(data){
EventBus.$emit("event-name",Data);
}
3 the last thing you want to do is listen on a component B.vue which you want to get the data or the event and fire a function to do something with the data you passed
import {EventBus} from "#/service/EventBus.js";
created() {
EventBus.$on("gallery-created",(Data) =>{
this.MyFunction(Data);
})
},
By doing this you can pass an event and data to any component using event bus method.

How to call a method in a child (or any other) component after an event in the parent?

A question like this:
How is it possible in vue3 after an event to call a method in another component?
Found this solution for vue2
this.$refs.myComponent.myMethod()
But how is it supposed to work without "this" in vue3 + composition api?
If this is not possible then what are the alternatives?
The event itself occurs in the following code:
(for example, we can take the resize event of a component and set a task - after it, activate a method inside another component)
<Vue3DraggableResizable class="chartApp"
#resizing="print('resizing')"
>
How is it possible to implement it?
Not sure about if they are best practices but here we go.
I think you can watch your props change and trigger an event like this
import { watch, toRefs } from 'vue'
const props = defineProps({
yourReactiveProp: Boolean
})
const { yourReactiveProp } = toRefs(props) // this is reactive
watch(
() => yourReactivePropn.value,
(newVal) => {
yourMethod() // you can pass the new value also.
})
You can use mitt package. Literally, you can listen and fire emit from anywhere to anywhere. I.e, parent-child, child-parent, siblings, non-siblings, etc..

How to detect when vue component is after update function by property?

I need to display a spinner in vue for every component (this is the requirement).
For that I think about to do v-if="loading" inside component HTML.
My question is how to detect when component is loading complete? (meaning after the DOM is rendered, and the data-bind is resolved to the DOM elements)
According to Vue lifecycle when update function is trigger then the render is complete.
So for that I'll need to implement for every component update function that change the loading to false. is there eazy way to do that? for example one place to write the update function? can I do that without implement extends? any sophisticated way to do that?
What I need for example is this.loading available in Vue instance.
Of course you can do it. Vue mixins come in rescue.
Mixins are a flexible way to distribute reusable functionalities for
Vue components. A mixin object can contain any component options. When
a component uses a mixin, all options in the mixin will be “mixed”
into the component’s own options.
Notice that you should use mounted hook if you want to track when the component is inserted into DOM.
Called after the instance has been mounted, where el is replaced by
the newly created vm.$el. If the root instance is mounted to an
in-document element, vm.$el will also be in-document when mounted is
called.
mixin.js:
export default {
data() {
return {
loading: true
}
},
mounted() {
console.log('I have been mounted')
this.loading = false
}
}
And then register that mixin globally, so it will be available in all components:
import mixin from './mixin'
Vue.mixin(mixin)

Is it possible to send an event from one component to another without using a global EventBus in Vue.js?

I have two components that do not have a parent - child relationship. They are not related to each other. But I am trying to make it so that when an event is emitted from one component then the other component can listen to that event and perform an action.
Here is an example, let's say that I have a component called "reset-component" and at some point it will emit an event called "reset".
<reset-component #reset="actionReset" />
And I also have a "grid-component" that should listen to the "reset" action emitted by the "reset-component" and perform some action.
<grid-component />
What are my options do accomplish this? The only solution that I can think of is to emit a global event using an EventBus and then have the grid-component listen to that global event. But is that a good idea? Isn't that more of an anti-patern?
// Reset component
EventBus.$emit('reset')
// Grid component
created()
{
EventBus.$on('reset', () => {
doSomething()
})
}
You can use Subject to accomplish this. See this link for more information: Vue.js + RxJS - Communicating Between Components with Observable & Subject
You actually have multiple options. But as you already answered yourself a global event bus is one of them
In your main file (assume something like main.js) you could do
Vue.prototype.$bus = new Vue()
By doing so you can now access this.$bus in every component e.g.
methods: {
dispatchReset () {
this.$bus.$emit('reset')
}
}
And other components can listen to it via
created () {
this.$bus.$on('reset', this.doReset)
}
Vuex is another option that was already mentioned by #Armen Armus in the comments. You could hold a global state for your application and dispatch events from within every component you want. However, if you really only want a few events like a reset I don't see a reason to add vuex.
Another option could also be to simply hold a "state" in your root Vue instance / component, but this would require you do pass down props and pass up events from within every view / component - and that would be quite annoying.
When passing props to child components and emitting events to parent components become hard because of distance or app complexity you can use VueX :
https://vuex.vuejs.org/
Yes you can use vm.$on and vm.$emit within components.
In your reset-component trigger the event from any method or element
this.$emit('reset')
and then you can then listen it,
<reset-component #reset="actionReset" />
You can also just add to the instances hooks:
ResetComponent.$on("reset", doSmth)
vm.$on
But i guess this will create a relation.

Vue // this.$root.$off unsubscribes all instances of the component from the global event

I have a custom input validation component that I use in a form. Something like 15 instances of this component around the app. It has a beforeDestroy method in which I unsubscribe from global event called triggerGlobalValidation which triggers validation before I send request to server. As expected it's triggered only once inside this certain component.
There is a container with v-if parameter which contains one instance of the component. So when v-if="false" I expect this certain component to unsubscribe from event and get destroyed. It goes well accept for one thing: somehow this component unsubscribes ALL other instances of it from the triggerGlobalValidation event as well.
I've tested the behavior with v-show and it works as expected - all other instances keep subscribed, but since the v-show field is required for the form it's blocking validation even without being shown in the DOM. I also tested above mentioned components behavior by removing the this.$root.$off("triggerGlobalValidation") and it also works as expected + polluting the global root.
Vue documentation on $off method is saying:
If no arguments are provided, remove all event listeners;
If only the event is provided, remove all listeners for that event;
If both event and callback are given, remove the listener for that
specific callback only.
Is it possible to somehow mention in the callback, that this $off method shouldn't unsubscribe all of its instances from the event, but just this certain one being destroyed?
Check it out in codesandbox
As answered in the issue, you need to save the handler and pass it again to $off
mounted() {
this.fn = () => {
this.toggleWarning();
}
this.$root.$on("triggerChildComponents", this.fn);
},
beforeDestroy() {
this.$root.$off("triggerChildComponents", this.fn);
},

Categories