It seems that when I try to emit an event in mounted it doesn't get triggered! therefore It doesnt show up in devtools
main.js
const app = new Vue({
el: '#app',
mounted() {
console.log('asd');
this.$emit("clicked", "someValue");
}
});
--
<body>
<div id="app">
<h1>vuejs</h1>
</div>
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<script src="main.js"></script>
</body>
Why is that? and how can I do it?
It works fine - your console.log outputs as it should, and you're emitting the event without issues.
In the code you've shown, though, you haven't set anything up to listen to the emitted event... Here's a simple, contrived example of how to do that with this.$on:
const app = new Vue({
el: '#app',
mounted() {
console.log('asd');
this.$on('clicked', function(value) {
alert('clicked!' + value);
});
this.$emit("clicked", "someValue");
}
});
Demonstration: https://codepen.io/anon/pen/qKeWxJ
When you add an event handler in the parent's mounted lifecycle event
for an event that is emitted in a child's mounted event, the handler
will not catch the event emitted by the child because the handler is
added after the child has already emitted the event. Basically the
cycle of events is like this.
Parent's create Child's create Child's mounted Parent's mounted
Obviously there are other lifecycle events that occur, but that's the
sequence that matters in this case.
If you want to handle an event emitted in the child, you need to
create the handler (call $on) before the child's mounted event.
Is it possible to emit from mounted in Vuejs?
Related
I am emitting an event in a child component so I can, if I need to, prevent it from happening in the parent.
Is there a way to retrieve event object so I can prevent it in the parent component like this ?
// child component event
this.$emit('navigationTab');
// parent template event bind
#onTab="on_tab($event)"
// parent handler method
on_tab(event) {
event.preventDefault
// ...logic etc
}
It only works if y pass the event object like this I wish to catch $event directly without sending the object inside the event, which is probably already sent in the emit
// child component event
this.$emit('navigationTab', event);
// parent template bind
#navigationTab="on_tab"
// parent handler method
on_tab(event) {
event.preventDefault
// ...logic etc
}
Event argument is automatically passed as an parameter into handler method (see example bellow)
You can't use preventDefault() on custom events, only on native browser events. And of course you don't need to because there is not any "default" behavior for custom events (emitted with $emit)
const myComponent = Vue.component('myComponent', {
template: `
<div>
<button #click="$emit('myEvent', 'event payload')">Click me!</button>
</div>
`
})
const app = new Vue({
components: {myComponent} ,
template: `
<div>
<myComponent #myEvent="handle" />
</div>
`,
methods: {
handle(event) {
console.log("Event received:", event)
}
}
})
app.$mount("#app")
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>
component 1
getMyProfile(){
this.$root.$emit('event');
console.log("emited")
},
component 2
mounted() {
this.$root.$on('event', () = {
alert("Fired");
}
}
I am trying to alert "fired" of comonent 2 from component 1. But this is not happening. what i am doing wrong. Should i have to add something on main js?
Other than the small typo in your $on, it's not clear what you're doing wrong, as you haven't provided enough context, but here's a working example of exactly what you're trying to do (send and receive an event via the $root element, without instantiating a separate eventbus Vue instance). (Many people do prefer to do the message passing via a separate instance, but it's functionally similar to do it on $root.)
I included a payload object to demonstrate passing information along with the event.
Vue.component('sender', {
template: '<span><button #click="send">Sender</button></span>',
methods: {
send() {
console.log("Sending...")
this.$root.$emit('eventName', {
message: "This is a message object sent with the event"
})
}
}
})
Vue.component('receiver', {
template: '<span>Receiver component {{message}}</span>',
data() {return {message: ''}},
mounted() {
this.$root.$on('eventName', (payload) => {
console.log("Received message", payload)
this.message = payload.message
})
}
})
new Vue({
el: '#app'
});
<script src="https://unpkg.com/vue#latest/dist/vue.min.js"></script>
<div id="app">
<sender></sender>
<receiver></receiver>
</div>
Personally I don't tend to use this pattern much; I find it's better to handle cross-component communication from parent to child as props, and from child to parent as direct $emits (not on $root). When I find I'm needing sibling-to-sibling communication that's generally a sign that I've made some poor architecture choices, or that my app has grown large enough that I should switch over to vuex. Piling all the event messaging into a single location, whether that's $root or an eventbus instance, tends to make the app harder to reason about and debug.
At the very least you should be very explicit in naming your events, so it's easier to tell where they're coming from; event names such as "handleClick" or just "event" quickly become mysterious unknowns.
So what you are looking for is an event bus (global events)
I'd advise considering using vuex anytime you have the need to implement an event bus.
Let's get back to the problem.
Create a file event-bus.js this is what's going to be capturing and distributing events.
import Vue from 'vue'
const EventBus = new Vue()
export default EventBus
Now in your main.js register your event bus
import Vue from 'vue'
import eventBus from './event-bus'
//other imports
Vue.prototype.$eventBus = eventBus
new Vue({
...
}).$mount('#app')
Now you can:
listen for events with this.$eventBus.$on(eventName)
emit events this.$eventBus.$emit(eventName)
in this example i'll bring event from child to parent component with $emit
Child Component:
Vue.component('Child component ', {
methods: {
getMyProfile: function() {
this.$emit('me-event', 'YUP!')
}
},
template: `
<button v-on:click="getMyProfile">
Emmit Event to Parrent
</button>
`
})
Parent Component:
Vue.component('Parent component ', {
methods: {
getEventFromChild: function(event) {
alert(event)
}
}
template: `
<div>
<Child-component v-on:me-event="getEventFromChild" />
</div>
`
})
for example when you have data flow one way from parent to child and you want to bring data from child to parent you can use $emit and bring it from child.. and in the parent you must catch it with v-on directive. (sry form my english)
If component 2 is the parent of the component 1 you could do:
getMyProfile(){
this.$emit('myevent');
console.log("emited")
},
for componant 2 componant like
<componant-2 #myevent="dosomething" ...></componant-2>
and in componant two
methods: {
dosomething() {
console.log('Event Received');
}
}
I opened a similar topic a few days ago, where I was suggested to use beforeRouteLeave within the route component definition.
However, I'm creating a Vue component and I won't have control over how developers wants to define their route components. Therefore, I need a way to fire an event within my own component and don't rely on external route components.
When changing from one route to another, the beforeDestroy gets fired after the DOM structure changes.
I've tried using beforeUpdate and updated events on my component definition, but none seems to fire before the DOM changes.
import Vue from 'vue'
import MyComponent from '../myComponent/' // <-- Need to fire the event here
import router from './router'
Vue.use(MyComponent)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
}).$mount('#app')
In the Vue instance lifecycle, the hook beforeDestroy gets called once the DOM has changed.
You are most likely looking for a beforeUnmount hook, which would be in-between mounted and beforeDestroy, but that is not available:
However, you could take advantage of JavaScript hooks. There is a JavaScript hook called leave, where you can access the DOM before it changes.
leave: function (el, done) {
// ...
done()
},
For this to work, you would need to wrap your element in a <transition> wrapper component.
ie.
<transition
:css="false"
#leave="leave"
>
<div>
<!-- ... -->
</div>
</transition>
...
methods: {
leave(el, done) {
// access to DOM element
done()
}
}
If one function is bound to multiple Vue event hooks, is there any way to know which event triggered the function?
Here's a contrived example:
<div v-on:click="handler" v-on:mouseover="handler">
Which event called handler? Click or Mouseover?
</div>
This could also help understand the context for Transition hooks and determining which was triggered.
<transition appear v-on:appear="handler" v-on:enter="handler">
From inside handler, can we tell which hook was the caller?
A practical workaround for these situations would be decorating common functionality behind two simple wrapper functions, but it seems like Vue probably has a way of understanding its event calling context which might have been overlooked.
The event object's type will tell you which event triggered the handler. You can get the event object in Vue using the $event variable:
Vue.component('demo', {
template: '<div v-on:click="handler($event)" v-on:mouseover="handler($event)">Click or hover on me</div>',
methods: {
handler(e) {
console.log(e.type); // e is the event object passed in as $event
}
}
});
new Vue({
el: '#app'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.0/vue.js"></script>
<div id="app">
<demo></demo>
</div>
I have three nested components
<parent #event="handle">
<inbetween #event="$emit('event')">
<child> // emits event
</child>
</inbetween>
</parent>
so, when a child emits the event, I have to add that annoying part #event="$emit('event')" to the component inbetween, otherwise parent does not receive any event.
I hope it is not supposed to work that way. I wonder what could be wrong with it?
Yes thats how its supposed to work. Events go only from child to parent, it does not go from child up to grand parent. Therefore you have to emit the event from both child and inbetween component.
If you want to avoid this method you can use Event Bus:
https://medium.com/#andrejsabrickis/https-medium-com-andrejsabrickis-create-simple-eventbus-to-communicate-between-vue-js-components-cdc11cd59860
alligator.io/vuejs/global-event-bus
This is actually intentional. The reasoning is, when looking at the code for one component and you see that it's listening to an event, you can then look at the template to see where that event is coming from. If events could reach a component arbitrarily deep, it would be harder to figure out how and from where that event is being triggered.
However, Vue used to have a way of doing what you want to do, through the methods $broadcast and $dispatch, and they were eventually removed for the reasons talked about here. Here's a page from the docs which explains why, along with possible solutions, such as using a global event bus, or a centralized state management solution such as Vuex.
Vue custom events don't bubble.
The recommended way to handle ancestor/sibling communication in complex cases is using Vuex. If you have simple needs you can create Vue instance to use as event hub.
You would create a global variable:
var eventHub = new Vue(); // use a Vue instance as event hub
To emit events you would use in any component:
eventHub.$emit('myevent', 'some value');
And, to listen to that event, again, in any component, do:
eventHub.$on('myevent', (e) => {
console.log('myevent received', e)
});
Demo:
var eventHub = new Vue(); // use a Vue instance as event hub
Vue.component('parent', {
template: "#parent-tpl",
created() {
eventHub.$on('event', (e) => {
console.log('event received at parent! value:', e);
});
}
});
Vue.component('inbetween', {
template: "#inbetween-tpl"
});
Vue.component('child', {
template: "#child-tpl",
methods: {
emitEvent() {
eventHub.$emit('event', 123);
}
}
});
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
}
})
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<template id="parent-tpl">
<div>
<inbetween></inbetween>
</div>
</template>
<template id="inbetween-tpl">
<div>
<child></child>
</div>
</template>
<template id="child-tpl">
<div>
<h1>I'm the child</h1>
<button #click="emitEvent">Trigger EVENT</button>
</div>
</template>
<div id="app">
<p>{{ message }}</p>
<parent></parent>
</div>
Note: If creating a dedicated instance as event hub is something complicated in your environment, you can replace eventHub with this.$root (inside your components) and use your own Vue instance as hub.