Get Off My Lawn
Get Off My Lawn

Reputation: 36311

Get vue component from event

I am building a drag and drop feature in my application where the drop area looks like this:

<template>
  <div class="layers-panel" @dragover="onDragOver" @drop="onDragDrop">
    <layer
      v-for="(layer, index) in layers"
      :key="layer.name"
      :info="layer"
      :offset="index"
      ref="layer"
    ></layer>
  </div>
</template>

Then the draggable items look like this:

<template>
  <div class="layer" draggable="true" @dragstart="onDragStart">
    <!-- Content of draggable element -->
  </div>
</template>

In my drag over event, I want to get the current component that the element relates to, so when I drag an item over the <div class="layer"...> element I would like to get that component. I am not sure how to get access to this since an event is passed to the @dragover="onDragOver" event which only gives me access to html elements and not Vue components.

This is what my current dragover event looks like:

    public onDragOver(evt: DragEvent) {
      evt.preventDefault()
      if (!evt.target) return
      let layer = (evt.target as HTMLElement).closest('.layer') as HTMLElement
    }

How can I get the component that that element belongs to?

Upvotes: 4

Views: 2978

Answers (1)

xlm
xlm

Reputation: 7594

Vue certainly allows you to access a component instance however I don't think it's necessary to reaching your actual goal: reordering of components via drag-and-drop.

Drag-and-drop means we're limited by what's afforded by DragEvent:

You can't determine where the element is dropped because your drop element contains all the layers. Instead if each layer is a drop element, then you can determine which one it is via dragover via event.target.

We also need to identify which element corresponding to the layers array we're dragging/dropping on. This can be achieved by binding the index of each layer as an attribute so we can access it via event.target.

To keep it simple, I'll use the id attribute so I can just access it later via event.target.id:

<div
  v-for="(layer, idx) in layers"
  :key="idx"
  :id="idx"
  draggable="true"
  @dragstart="dragstart"
>
  <div
    :id="idx"
    class="draggable"
    @dragover="dragover"
    @drop="drop"
  >
    {{ layer }}
  </div>
</div>

Note I bind the same index to id so I can correctly identify the target layer.

We'll also need to ensure we can identify the element that was dragged since on drop we'll only have the drop element via event.target. We can do this by setting data in DataTransfer when we first start the drag:

dragstart(event) {
  event.dataTransfer.setData("selectedIdx", event.target.id);
},

Now on drop we have access to both and can manipulate layers as we desire:

drop(event) {
  var selectedIdx = event.dataTransfer.getData("selectedIdx");
  var targetIdx = event.target.id;
  var selected = this.layers.splice(selectedIdx, 1)[0];
  this.layers.splice(targetIdx, 0, selected);
},

Example codepen

Upvotes: 2

Related Questions