Out of the blue, emitter stopped working:
event-bus.js
import Vue from 'vue';
export const EventBus = new Vue();
import { EventBus } from '../event-bus';
...
mounted() {
this.getCart();
}
...
methods: {
getCart() {
axios.get(`${app.api_erp_url}/cart/${this.cartId}`).then((response) => {
this.cart = response.data;
EventBus.$emit('cartLoaded', this.cart); // this not working
});
}
},
another-component.vue
mounted() {
// MiniCart.vue
EventBus.$on('cartLoaded', (payload) => {
...
});
},
No matter how I try to emit the event inside mounted/created, it will not work. No problems when firing events on click or something.
Created sandbox: https://codesandbox.io/s/gracious-kilby-m43ih?fontsize=14&hidenavigation=1&theme=dark
Child components mount before their parent component.
This is the sequence occurring in your example:
HelloWorld (parent) is created
Test (child) is created
Test (child) is mounted, which emits an event
HelloWorld (parent) is mounted, which subscribes to the event that was already emitted
If you want HelloWorld to catch the event from its children, subscribe to the event in the created hook.
demo
According to this You should use kebab-case format to name your custom events :
EventBus.$emit('cartLoaded', this.cart);//not correct
EventBus.$emit('cart-loaded', this.cart); //correct
May be the event emitted before MiniCart.vue component registered the event.
Meaning in code.
EventBus.$emit('cartLoaded', this.cart);
this run first time before the event has been registered
EventBus.$on('cartLoaded', (payload) => {
...
});
Related
I created a custom event and dispatched it in my main page script, which includes a Vue component. How do I listen to that event from inside the Vue component?
main.blade.php:
<body>
<insert-block></insert-block>
<script>
const insert_block_event = new CustomEvent('insert_block', { detail: 'some detail goes here' });
document.body.dispatchEvent(insert_block_event);
</script>
</body>
Since you're dispatching on document.body, your component could listen to the event on document.body with addEventListener in the mounted() hook, and removeEventListener in the beforeDestroy() hook:
// MyComponent.vue
export default {
mounted() {
document.body.addEventListener('insert_block', this.onInsertBlock)
},
beforeDestroy() {
document.body.removeEventListener('insert_block', this.onInsertBlock)
},
methods: {
onInsertBlock(e) {
console.log('insert_block', e)
}
}
}
If I have nested children that I want to propagate an event all the way up to the root component, is there any simple way to pass on the event?
i.e.
<root #custom_event="doSomething" #another_event="doSomethingElse">
<child1 #custom_event="passItAlong" #another_event="passItAlong">
<child2 #custom_event="passItAlong" #another_event="passItAlong">
<child3 #click="$emit('custom_event', 'data')">
</child3>
</child2>
</child1>
</root>
You have multiple options here:
You can use this.$root.$emit and then event will be sent to all components at once, you can listen to it as this.$root.$on
You can create eventBus as explained here and then use it wherever you need to:
// The most basic event bus
// Imprt vue.js
import Vue from 'vue';
// Create empty vue.js instance to use as event bus
const Bus = new Vue({});
// Export event bus instance
export default Bus;
// Using the most basic event bus
import Vue from 'vue';
import Bus from './basic';
Vue.component('my-first-component', {
methods: {
sampleClickAction() {
Bus.$emit('my-sample-event');
}
}
});
Vue.component('my-second-component', {
created() {
Bus.$on('my-sample-event', () => {
// Do something…
});
}
});
I'm using DeviceEventEmitter to handle events of a favorite method, to which is subscribed in the constructor:
DeviceEventEmitter.addListener("FavoriteClick", async (e) =>
{
// do something
})
This event listener stays active whenever the components unmounts (permenantly). What do I have to call to unsub? I've tried storing the event as a variable and calling listener.removeCurrentListener() in the componentWillUnmount() like the (limited) documentation states, if I understand that correctly, but removeCurrentListener() is not a method.
DeviceEventEmitter is deprecated, you should use NativeEventEmitter instead.
Example :
import { NativeEventEmitter, NativeModules } from 'react-native';
const { CalendarManager } = NativeModules;
const calendarManagerEmitter = new NativeEventEmitter(CalendarManager);
const subscription = calendarManagerEmitter.addListener(
'EventReminder',
(reminder) => console.log(reminder.name)
);
...
// Don't forget to unsubscribe, typically in componentWillUnmount
subscription.remove();
I have a Map component which initializes leaflet on the DOM like so:
Map.vue
<template>
<div ref="map"/>
<template>
<script>
import * as L from 'leaflet';
import mapEventBus from '../event-buses/map.vue';
export default {
mounted(){
const map = L.map(this.$refs.map);
mapEventBus.$on('add-marker',(newMarker) => {
newMarker.addTo(map);
});
}
}
</script>
And then I have another component which needs to add a marker that is built on the components creation.
OtherComponent.vue
<template>
<div/>
</template>
<script>
import mapEventBus from '../event-buses/map.vue';
export default {
created(){
mapEventBus.$emit('add-marker',L.marker([51.5, -0.09]));
}
}
</script>
Because the map is initialized after the OtherComponent has already tried emitting to the event bus, the event is never fired. What would be the best way to "await" for the map to be initialized and then add the marker to the map. I though about having a "cache" of pending markers that is added on the map creation but that seems clunky.
Example:
https://codesandbox.io/s/2ov71xnz3r
OK, so you've got a little chicken and egg problem there. You have an element you need to update via refs (some way to hack data into a 3rd party plugin), but you get the data BEFORE you mount the HTML.
What you need to do is separate out the immediate catch into a data variable, then on mount, check to see if it exists and if so update the HTML element.
I'm not answering your question above, because the problem is simplified in the codesandbox example you provided.
Here is the solution based on that:
https://codesandbox.io/s/3rnyp31n4p
<script>
import { EventBus } from '../eventBus.js'
export default {
data: () => ({
immediateMessage: null
}),
beforeCreate() {
EventBus.$on("immediate-message", message => {
this.immediateMessage = message;
});
},
mounted() {
if (this.immediateMessage) {
this.$refs.immediateMessageEl.innerHTML += this.immediateMessage;
}
EventBus.$on("delayed-message", message => {
this.$refs.delayedMessageEl.innerHTML += message;
});
}
};
</script>
Note, the beforeCreate() binds to the event and sets a variable, then we use that variable once the DOM is mounted.
Check out lifecycle hooks page for more info https://v2.vuejs.org/v2/guide/instance.html#Lifecycle-Diagram
This is definitely not the most elegant solution, but will definitely get you going.
I have a component with one method, which I'm firing on creation. It's using vue-select but purpose of this component shouldn't be relevant to my issue.
<template>
<v-select :on-change="onchangecallback"></v-select>
</template>
<script>
import Vue from 'vue'
import vSelect from 'vue-select'
Vue.component('v-select', vSelect);
export default {
methods: {
onchangecallback: () => {alert('default')}
},
created: function() {
this.onchangecallback();
}
}
</script>
In other file I'm importing this component and creating a new instance of it with Vue constructor and passing new onchangecallback method, which, by my understanding, should overwrite the default onchangecallback method:
import VSelect from './components/ui/VSelect.vue';
new Vue({
VSelect,
el: '#app',
components: {VSelect},
template: `<v-select />`,
methods: {
onchangecallback: () => {alert('custom')} // doesn't work
}
});
But when I start the app, instead of alert('custom') I still get alert('default').
I don't know what you are trying to achieve.
But here is my solution https://codesandbox.io/s/84qw9z13v9
You need to define a prop to pass your new callback function to that component (through the prop)
props: {
onchangecallback: {
type: Function,
default() {
return function() {
alert('default');
};
},
},
},
created: function() {
this.onchangecallback();
}
And the use it instead the default one.
Check all the code in that snippet.
For communication between components you're supposed to use events. Emit an event in a child component and make the parent component listen to it:
In the child component:
created() {
this.$emit('change')
}
In the parent component:
Template:
<child-component v-on:change="doSomething" />
Methods:
methods: {
doSomething() {
// ...
}
}
Explanation
In the child component you emit an event, in this case "change", whenever something changed. In your case this was upon creation of the component.
In the parent component you tell Vue that, whenever the child component emits a "change" event, it should run the method "doSomething" in your parent component.
This is used in many places, i.e. inputs, where the input emits an event "input" whenever its content changes and any parent can listen for that by using v-on:input="methodHere".