Error in directive dragula update hook: "TypeError: Cannot read property 'drake' of undefined"

663 Views Asked by At

I used vue-dragula in my project for the drag & drop list requirement.

It's worked perfect. But I have the below error at that hold element remove time.

Error in directive dragula update hook: "TypeError: Cannot read property 'drake' of undefined"

My code sample below

(template HTML code)

<ul v-dragula="dragDatas" bag="action-bag">
    <li v-for="(drDatas, drIndex) in dragDatas" :key="drDatas.id">{{drDatas.name}}</li>
</ul>

(Script code)

import 'vue-dragula/styles/dragula.css';

created () {
  Vue.vueDragula.options('action-bag', {
    invalid: function (el, handle) {
      return // CONDITION BASED
    }
  });
},

mounted () {
  const self = this;
  Vue.vueDragula.eventBus.$on('drop', async function ([bag, curElmt, allElmts]) {
    // drop event based functionalities
  })
}
2

There are 2 best solutions below

0
On BEST ANSWER

I fixed this issue with hide and show the parent component.

0
On

I believe that it will be much easier for you to use the vanilla Dragula library instead of the Vue-Dragula wrapper. This will save you from a lot of headaches and will also reduce the bundle size.

I am using it like this:

import dragula from 'dragula' 

data()
{
  return {
    stages: { // we have 5 lists - items can be dragged between any 2 of them
      LEAD_IN: [],
      HOMEOWNER_ADDED: [],
      PROPOSAL_GENERATED: [],
      WON: [],
      LOST: [],
    }, 
    drake: null,
  }
},
mounted()
{
    // IMPORTANT !!! prevent default image dragging for IE which interferes badly with Dragula
    document.ondragstart = function () {
      return false
    }
    this.drake = dragula({
      isContainer: this.dragContainer,
      moves: this.dragMoves,
      accepts: this.dragAccept,
      mirrorContainer: document.getElementById('app'), //eslint-disable-line
    })
    this.drake.on('drop', this.drakeDrop)
    this.drake.on('drag', this.drakeStart)
    this.drake.on('over', this.drakeOver)
    this.drake.on('out', this.drakeStop)
    this.drake.on('cancel', this.drakeStop) 
},
methods:
{
    dragContainer(el) {
      // defines containers for draggable items
      return 'kind' in el.dataset
    },
    dragMoves(el, source, handle, sibling) {
      // allows dragging only by using the HANDLE
      return 'move' in handle.dataset
    },
    dragAccept(el, target, source, sibling) {
      // only containers can accept draggable items
      return 'kind' in target.dataset
    },
    drakeStart(el, source) {
      // indicate which container's item we have started dragging
      source.classList.add('drag_src')
    },
    drakeOver(el, container, source) {
      source.classList.add('drag_src')
      container.classList.add('drag_dst')
    },
    drakeStop(el, container, source) {
      source.classList.remove('drag_src')
      container.classList.remove('drag_dst')
    },
    drakeDrop(el, target, source, sibling) {
      // EL was dropped into TARGET before SIBLING and originally came from SOURCE
      this.drake.cancel(true) // !!! very important - we do not want Dragula to mess with the DOM as it will confuse Vue
      if (source !== target) {
        // update arrays
        const task = this.stages[source.dataset.kind].splice(el.dataset.index, 1)[0]
        const dst = this.stages[target.dataset.kind]
        dst.splice(sibling ? sibling.dataset.index : dst.length, 0, task)
        this.updateTask(task, target.dataset.kind)
      }
    }, 
    updateTask(task, newStage) {
      this.loading = true
      this.$axios
        .post('/houses/stages', {
          lead_id: task.lead.id,
          kind: newStage,
        })
        .then(() => {
          this.loading = false
        })
        .catch((error) => {
          this.loading = false
          events.$emit(TOAST_ERROR, error.message || error)
        })
    }, 
}

Template for each of the lists is

<div :data-kind="kind" class="task_list">
  <div v-for="(task, idx) in list" :key="task.id" class="task_item" :data-index="idx">
    <div class="flexbox">
      <div class="content_move" data-move>this is the drag HANDLER</div>
      <button>some action, eventually</button>
    </div>
  </div>
</div>

The kind prop should match (in my particular case) with the properties of the this.stages object.

And some handy styles

.task_item {
  cursor: grabbing;
}
.task_list .task_item {
  cursor: default;
}
.task_list .task_item .content_move {
  cursor: move;
}
.task_list.drag_dst {
  border: 1px solid green;
}
.task_list.drag_src.drag_dst {
  border: 1px solid red;
}