I am working on a vue project and want to emit an event from a child ponent to a parent ponent after some time span (user hovers over element, and after a few seconds, the emit should happen).
I am pretty sure I have done emits similar to this in other ponents and that it has worked before, but now I get these two errors. I am not sure what has changed. I implemented vuex after my first try, came back to this and now I am not sure what happend? But maybe I just deleted something or I don't know. I also tried a console log just above the line that seems problematic and there the this value doesn't seem to be null. These are the errors I get:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'emit' of null" and after that the error Cannot read property 'emit' of null The first error mentions, that the error was found in the ponent, shown beneath.
Most stuff I saw related to this problem was about people doing es6 arrow functions incorrectly, so maybe the this.timer = setInterval(() => this.countdown(), 1000);
is problematic? I am not quite sure.
And here is the ponent (please excuse the messy code):
<template>
<div
:id="id"
class="board"
@dragenter.prevent
@dragover.prevent
@drop.prevent="drop"
@dragenter="dragenter($event)"
@dragover="dragover($event)"
@dragleave="dragleave($event)"
>
<slot class="row"/>
<div
:id="`ui-details-${id}`"
v-show="extendedHover">
Long hover
</div>
</div>
</template>
<script>
export default {
name: 'Devices',
props: ['id', 'acceptsDrop'],
data() {
return {
extendedHover: false,
timer: null,
totalTime: 2,
};
},
methods: {
drop(e) {
if (e.dataTransfer.getData('type') === 'layout') { // only accept dropped cards, not boards
const layoutId = e.dataTransfer.getData('layout_id');
this.$emit('dropped-layout', layoutId);
}
clearInterval(this.timer);
this.timer = null;
this.totalTime = 2;
console.log(e.dataTransfer.getData('card_id'));
e.target.classList.remove('hover-drag-over');
this.extendedHover = false;
// this.$emit('cancel-hover');
console.log('-------------dropped');
console.log(e);
console.log(e.dataTransfer.getData('type'));
/* const cardId = e.dataTransfer.getData('card_id');
console.warn('dropped onto device');
this.$emit('dropped-ponent', cardId);
e.target.classList.remove('hover-drag-over'); */
},
dragenter(e) {
// on dragenter we start a countdown of 1s
// over this value --> we see it as a long hover
console.log('------------dragenter');
console.log(e);
this.timer = setInterval(() => this.countdown(), 1000);
},
dragover(e) {
if (this.acceptsDrop) {
e.target.classList.add('hover-drag-over');
}
},
dragleave(e) {
if (this.acceptsDrop) {
clearInterval(this.timer);
this.timer = null;
this.totalTime = 2;
e.target.classList.remove('hover-drag-over');
this.extendedHover = false;
// this.$emit('cancel-hover');
}
},
countdown() {
this.totalTime -= 1;
if (this.totalTime === 0) {
this.extendedHover = true;
console.warn(this);
this.$emit('long-hover'); //this is the problematic line
}
},
},
};
</script>
<style scoped>
.board{
width: 100%;
min-height: 200px;
}
</style>
Thanks for any help!
Edit:
the output of the console.log is the following VueComponent {_uid: 18, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
there is some possibility to extend for more information if it is needed too.
Edit2: this ponent gets generated depending on an array in the parent ponent like this. So the slot only gets filled with a string for now.
<div
class = "col-4"
v-for="(device, index) in devices"
v-bind:key="`device-${device.ip}`"
>
<Devices
:id="`board-${device.ip}`"
v-bind:class="{'droparea-device':true, 'your-device': (index === thisDeviceIndex)}"
@dropped-layout="$emit('dropped-layout', $event, index)"
@long-hover="fetchAvailableUIsForDevice(device.device_name)"
@cancel-hover="cancelHover()"
:acceptsDrop=true
>
{{device.device_name}}
</Devices>
</div>
Edit3: For testing I also just wanted to try a very basic thing. So I added a Button with an on click event like this (but still there are errors):
<b-button
type="button"
variant="success"
v-on:click="$emit('long-hover')"
>
Control Center
</b-button>
I am working on a vue project and want to emit an event from a child ponent to a parent ponent after some time span (user hovers over element, and after a few seconds, the emit should happen).
I am pretty sure I have done emits similar to this in other ponents and that it has worked before, but now I get these two errors. I am not sure what has changed. I implemented vuex after my first try, came back to this and now I am not sure what happend? But maybe I just deleted something or I don't know. I also tried a console log just above the line that seems problematic and there the this value doesn't seem to be null. These are the errors I get:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'emit' of null" and after that the error Cannot read property 'emit' of null The first error mentions, that the error was found in the ponent, shown beneath.
Most stuff I saw related to this problem was about people doing es6 arrow functions incorrectly, so maybe the this.timer = setInterval(() => this.countdown(), 1000);
is problematic? I am not quite sure.
And here is the ponent (please excuse the messy code):
<template>
<div
:id="id"
class="board"
@dragenter.prevent
@dragover.prevent
@drop.prevent="drop"
@dragenter="dragenter($event)"
@dragover="dragover($event)"
@dragleave="dragleave($event)"
>
<slot class="row"/>
<div
:id="`ui-details-${id}`"
v-show="extendedHover">
Long hover
</div>
</div>
</template>
<script>
export default {
name: 'Devices',
props: ['id', 'acceptsDrop'],
data() {
return {
extendedHover: false,
timer: null,
totalTime: 2,
};
},
methods: {
drop(e) {
if (e.dataTransfer.getData('type') === 'layout') { // only accept dropped cards, not boards
const layoutId = e.dataTransfer.getData('layout_id');
this.$emit('dropped-layout', layoutId);
}
clearInterval(this.timer);
this.timer = null;
this.totalTime = 2;
console.log(e.dataTransfer.getData('card_id'));
e.target.classList.remove('hover-drag-over');
this.extendedHover = false;
// this.$emit('cancel-hover');
console.log('-------------dropped');
console.log(e);
console.log(e.dataTransfer.getData('type'));
/* const cardId = e.dataTransfer.getData('card_id');
console.warn('dropped onto device');
this.$emit('dropped-ponent', cardId);
e.target.classList.remove('hover-drag-over'); */
},
dragenter(e) {
// on dragenter we start a countdown of 1s
// over this value --> we see it as a long hover
console.log('------------dragenter');
console.log(e);
this.timer = setInterval(() => this.countdown(), 1000);
},
dragover(e) {
if (this.acceptsDrop) {
e.target.classList.add('hover-drag-over');
}
},
dragleave(e) {
if (this.acceptsDrop) {
clearInterval(this.timer);
this.timer = null;
this.totalTime = 2;
e.target.classList.remove('hover-drag-over');
this.extendedHover = false;
// this.$emit('cancel-hover');
}
},
countdown() {
this.totalTime -= 1;
if (this.totalTime === 0) {
this.extendedHover = true;
console.warn(this);
this.$emit('long-hover'); //this is the problematic line
}
},
},
};
</script>
<style scoped>
.board{
width: 100%;
min-height: 200px;
}
</style>
Thanks for any help!
Edit:
the output of the console.log is the following VueComponent {_uid: 18, _isVue: true, $options: {…}, _renderProxy: Proxy, _self: VueComponent, …}
there is some possibility to extend for more information if it is needed too.
Edit2: this ponent gets generated depending on an array in the parent ponent like this. So the slot only gets filled with a string for now.
<div
class = "col-4"
v-for="(device, index) in devices"
v-bind:key="`device-${device.ip}`"
>
<Devices
:id="`board-${device.ip}`"
v-bind:class="{'droparea-device':true, 'your-device': (index === thisDeviceIndex)}"
@dropped-layout="$emit('dropped-layout', $event, index)"
@long-hover="fetchAvailableUIsForDevice(device.device_name)"
@cancel-hover="cancelHover()"
:acceptsDrop=true
>
{{device.device_name}}
</Devices>
</div>
Edit3: For testing I also just wanted to try a very basic thing. So I added a Button with an on click event like this (but still there are errors):
<b-button
type="button"
variant="success"
v-on:click="$emit('long-hover')"
>
Control Center
</b-button>
Share
Improve this question
edited May 19, 2020 at 4:20
NotARobot
asked May 18, 2020 at 18:51
NotARobotNotARobot
5253 gold badges12 silver badges28 bronze badges
8
-
And what output from
console.warn(this);
? – Anatoly Commented May 18, 2020 at 18:57 - @Anatoly, i added the output to the bottom of the problem description. Does it help like this already? – NotARobot Commented May 18, 2020 at 19:17
-
Will the issue disappear if you call
this.$emit('long-hover');
in dragenter directly? – Anatoly Commented May 18, 2020 at 19:33 - I tested it with this line this.timer = setInterval(() => this.$emit('long-hover'), 1000); and the same error remains. Also I tried getting rid of the timer pletely and test it with just this.$emit('long-hover') and the error also was still there. Which I find kind of strange... – NotARobot Commented May 18, 2020 at 19:41
- Are you sure that the error in THAT $emit call and not about another one? – Anatoly Commented May 18, 2020 at 19:45
3 Answers
Reset to default 2So thanks for your help witih this strange behavior. After testing the emit with a button and it still not working, I thought that this was very suspicious. Just to make sure, I took a look into the parent ponent because there I execute a function when the child emits the event. When I looked into the function, I had the following line:
this.socket.emit('fetch_available_uis', { device_name: device });
And here the error description fitted! Because as I said, I included Vuex and after that it didn't work anymore. When adding vuex, I also moved the socket connection to the store so now there was indeed no this.socket or better to say the null value that was mentioned above. I fixed this line and the error now is gone!
So for others with similar problems in the future, I remend to look at functions in parents that receive your childs event and check for mistakes there. Because even though the error was in the parent, the error messages still showed that the error was found in the child probably because it took the ponent where the event was emitted as the source and not the ponent above.
The reason I think yes may e from the line as you suggest. this.countdown
points to nowhere because this
pointer here is not belongs to Vue
this.timer = setInterval(() => this.countdown(), 1000);
To fix, I suggest you can try
dragenter(e) {
// on dragenter we start a countdown of 1s
// over this value --> we see it as a long hover
console.log('------------dragenter');
console.log(e);
let vm = this; // assign Vue this to vm
this.timer = setInterval(() => vm.countdown(), 1000); // use vm instead of this
},
For anyone who came here from a reach of TypeError: Cannot read properties of null (reading 'emitsOptions')
In my case I used the toRaw()
function and forgot to import it from vue
:
import { toRaw } from 'vue';