2

I am working on a vue project and want to emit an event from a child component to a parent component 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 components 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 component, 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 component (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-component', 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 component gets generated depending on an array in the parent component 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>
8
  • And what output from console.warn(this);? 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? Commented May 18, 2020 at 19:17
  • Will the issue disappear if you call this.$emit('long-hover'); in dragenter directly? 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 completely and test it with just this.$emit('long-hover') and the error also was still there. Which I find kind of strange... Commented May 18, 2020 at 19:41
  • Are you sure that the error in THAT $emit call and not about another one? Commented May 18, 2020 at 19:45

3 Answers 3

2

So 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 component 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 recommend 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 component where the event was emitted as the source and not the component above.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the posting this answer, got the same error, and there was indeed a property in my parent that was null. Cause of you, I looked at the right place.
0

The reason I think yes may come 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
    },

2 Comments

Unfortunately I still the same error if I try it like this, do you have any other idea?
Also I tried something very basic like adding the emit on a button like I did in other components. I added it to my problem description.
0

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';

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.