DEV Community

100
100

Posted on

Yet Another Vue Drag and Drop

This post was first published here.

I had spent an awfully long time figuring out how to create a drag-and-drop file input in our Vue app. Most of the articles handled drag-and-drop, but not a standard file picker. I wanted both, kind of like Dropzone.

I’ve linked the complete code at the bottom.

This post won’t cover the part where the file(s) are uploaded to a server, or the subsequent handling of that; too many people have written about that.

First let’s set up the app.

<main>
  <div id='app'>
    <div class="container">
      Add your files here:
      <br>
      <input type="file" name="file-input" multiple="True">
    </div>
  </div>
</main>

We already have a file input button; we’ll store the file in a variable called files.

var app = new Vue({
  el: '#app',
  data: {
    files: [],
  },
})

Prevent Default Behaviour

First things first, what I don’t want is the user to drag over a file and have that file open in the browser (the default behaviour). To prevent this, we can add a directive in the <div id="app"> itself. Thanks for the tip, Raymond Camden.

I add a directive to prevent the default behaviour of dragover and drop events in the entire app. However, I only want the user to be able to drop the files in the container area around the file input. Once this happens, the handleFileDrop method is called. I’ve also added an on-change directive to handle file uploads by the input button.

I’ve also added a list of all the files stored to get an idea of what’s going on. We’ll modify this list later.

<div id="app" @dragover.prevent @drop.prevent>
  <div class="container" @drop="handleFileDrop">
    Add your files here:
    <br>
    <input type="file" name="file-input"
        multiple="True" @change="handleFileInput">
    <ul>
      <li v-for="file in files">
        {{ file.name }} ({{ file.size }} b) 
     </li>
    </ul>
  </div>
</div>

Handle File Inputs

Let’s code up these two methods we’ve defined. (Most of this has been modified from this article.)

var app = new Vue({
  el: '#app',
  data: {
    files: [],
  },
  methods: {
    handleFileDrop(e) {
      let droppedFiles = e.dataTransfer.files;
      if(!droppedFiles) return;
      ([...droppedFiles]).forEach(f => {
        this.files.push(f);
      });
    },
    handleFileInput(e) {
      let files = e.target.files
        if(!files) return;
        ([...files]).forEach(f => {  
          this.files.push(f);
        });
    },
  }
})

This handles the file inputs both by the conventional method; and by the cooler drag-and-drop way.

Finishing touches

Notice something odd yet? We’ve got an out-of-place file input. Dropping files into the container does not affect this input, which still shows No files selected.

To remedy this, we wrap the input file with a div and style it, as explained in this kind of hack-y but ingenious answer.
Add your files here:

      <br>
      <div class="file-wrapper">
        <input type="file" name="file-input"
            multiple="True" @change="handleFileInput">
        Click or drag to insert.
      </div>
.file-wrapper {
    text-align: center;
    width: 200px;
    height: 3em;
    vertical-align: middle;
    display: table-cell;
    position: relative;
    overflow: hidden;
    background: gray; /* and other things to make it pretty */
}
.file-wrapper input {
    position: absolute;
    top: 0;
    right: 0;
    cursor: pointer;
    opacity: 0.0;
    filter: alpha(opacity=0);
    font-size: 300px;
    height: 200px;
}

Removing files

This is where we modify our list. So far, we’ve allowed only addition of files. For removal, we use the index of the file in the array and pass it on to a removeFile method, which just pops that element from the array.

<ul>
  <li v-for="(file, index) in files">
    {{ file.name }} ({{ file.size }} b)
    <button @click="removeFile(index)"
        title="Remove">X</button>
  </li>
</ul>
  methods:{
    removeFile(fileKey){
      this.files.splice(fileKey, 1)
    }
  // Other methods previously defined
}

That’s it. We have an interface for file upload which allows both click-and-select, and drag-and-drop inputs in a Vue application.

The complete code is available on Codepen, do check it out.

Top comments (2)

Collapse
 
bensunvalues profile image
bensunvalues

Hey man, great post! I thought this was rather elegant and simplistic. It was easy enough for me to follow.
Thanks again!

Collapse
 
gsusmonzon profile image
Jesus Monzon

Thanks for the post, has been an inspiration for adding drag&drop support in a web app of mine