/vue-clip

Simple and hackable file uploader for VueJs. Supports Vue >= 2.1

Primary LanguageJavaScript

Introduction

Vue clip is a minimalistic and hackable file uploader for VueJs. I wrote this plugin due to the absence of well written file uploaders with fine-grained controls.

Version Build Status Downloads License

Features

  1. Written in vanilla Javascript.
  2. Weighs 17.9KB ( Minified and Gzip ), 57KB ( Minified ).
  3. Hackable to the core with custom events.
  4. Does not pollute DOM by adding unnecessary markup. Infact the component will create a single div element.

Quick Intro

<iframe src="https://www.youtube.com/embed/84_SwbPWjKo" type="text/html" width="640" height="360" frameborder="0"></iframe>

Setup

You can make use of module by installing it from npm or directly using it from CDN.

Npm

npm i --save vue-clip
import Vue from 'vue'
import VueClip from 'vue-clip'

Vue.use(VueClip)

Globally

Also, you can reference the script file via CDN which will add a global component called vue-clip to the Vue instance.

Basic Usage

<template>
  <vue-clip :options="options">
    <template slot="clip-uploader-action">
      <div>
        <div class="dz-message"><h2> Click or Drag and Drop files here upload </h2></div>
      </div>
    </template>

    <template slot="clip-uploader-body" scope="props">
      <div v-for="file in props.files">
        <img v-bind:src="file.dataUrl" />
        {{ file.name }} {{ file.status }}
      </div>
    </template>

  </vue-clip>
</template>

<script>
  export default {

    data () {
      return {
        options: {
          url: '/upload',
          paramName: 'file'
        }
      }
    }

  }
</script>

Configuration Options

Option Possible Values Description
url String, Function Url to be used for uploading files. This can be a string or a function ( in case your URL is dynamic )
method String, Function Http method to be used. Defaults to post.
parallelUploads Number Number of files to be uploaded in parallel.
maxFilesize Number, Object The file size in MB to be allowed. Also, you can pass an object with limit and error message.
paramName String Param name to be used for uploading file(s). Defaults to file.
uploadMultiple Boolean Whether or not to upload multiple files.
headers Object Http headers to be sent along each request.
maxFiles Number, Object a maximum number of files to be uploaded. You can also pass an object with limit and error message.
acceptedFiles Array, Object File types to be accepted. ['image/*', 'application/pdf'].
accept Function A custom function to be used for validating each file upload. This method receives a done callback. In the case of any errors, you must call done with a single error argument.

maxFilesize

The maxFilesize option defines the size of the file to be checked for when uploading files.

{
  maxFilesize: 1 // 1mb
}

// or

{
  maxFilesize: {
    limit: 1,
    message: '{{ filesize }} is greater than the {{ maxFilesize }}'
  }
}

maxFiles

The maxFiles option defines the maximum number of files to be uploaded.

{
  maxFiles: 5
}

// or

{
  maxFiles: {
    limit: 5,
    message: 'You can only upload a max of 5 files'
  }
}

acceptedFiles

The acceptedFiles option defines the mime types of files to be accepted.

// as an array of mime types

{
  acceptedFiles: ['image/*', 'application/pdf']
}

// as an object with an array of mime types
// and a custom error message

{
  acceptedFiles: {
    extensions: ['image/*'],
    message: 'You are uploading an invalid file'
  }
}

// as a plain, comma-delimited string

{
  acceptedFiles: 'image/*,application/pdf'
}

accept

The accept is a low-level method to run manual validations and return a formatted error string ( in the case of error).

{
  accept: function (file, done) {
    if (file.size > (1024 * 1024)) {
      done('File must be smaller than 1MB')
      return
    }

    done()
  }
}

Dragging

The most common requirement is to know when a user starts and stops dragging a file so that you can add some visual feedback to the UI. The easiest way is to make use of Scoped slots.

<template>
  <vue-clip :options="options">

    <template slot="clip-uploader-action" scope="params">
      <div v-bind:class="{'is-dragging': params.dragging}" class="upload-action">
        <h2> Click or Drag and Drop files here upload </h2>
      </div>
    </template>

  </vue-clip>
</template>

<style>
  .upload-action.is-dragging {
    background: green;
  }
</style>

Events

You can make use of vue-clip without writing any javascript code, but if you want low-level control over the upload behavior, consider listening to special events.

onInit(uploader)

Called every time the vue-clip is initiated and binds to DOM.

<template>
  <vue-clip :on-init="init">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      init (uploader) {
        // javascript uploader instance
      }
    }

  }
</script>

onAddedFile(file)

This event is invoked every time a new file gets uploaded. You can listen for this event, you want to have access to each file object within your own parent component.

<template>
  <vue-clip :on-added-file="addedFile">
  </vue-clip>
</template>

<script>
  export default {

    data: function () {
      return {
        files: []
      }
    }

    methods: {
      addedFile (file) {
        this.files.push(file)
      }
    }

  }
</script>

onRemovedFile(file)

This event is invoked every time the file has been removed. This is the nice place to make a request to your server for deleting the file.

<template>
  <vue-clip :on-removed-file="removedFile">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      removedFile (file) {
        this
        .$http
        .post(`delete/${file.customAttributes.id}`)
        .then(console.log)
        .catch(console.error)
      }
    }

  }
</script>

onSending(file, XHR, formData)

This event is emitted before making the upload HTTP request. So this is the time to modify the HTTP request and send some custom attributes.

<template>
  <vue-clip :on-sending="sending">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      sending (file, xhr, formData) {
        formData.append('_csrf', '<token>')
      }
    }

  }
</script>

onComplete(file, status, xhr)

This event is called when a file has been processed. It includes error, success both. 3rd argument will be the xhr response, if the error is returned from the server when uploading the file.

<template>
  <vue-clip :on-complete="complete">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      complete (file, status, xhr) {
        // Adding server id to be used for deleting
        // the file.
        file.addAttribute('id', xhr.response.id)
      }
    }

  }
</script>

onDragEnter

This event is invoked as soon as the user starts dragging the file.

<template>
  <vue-clip :on-drag-enter="dragging">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      dragging () {
        // user started dragging the file.
      }
    }

  }
</script>

onDragLeave

This event is invoked when the user stops dragging the file.

<template>
  <vue-clip :on-drag-leave="stoppedDragging">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      stoppedDragging () {
        // user stopped dragging the file.
      }
    }

  }
</script>

onDrop

This event is invoked when the user drops a file on the vue-clip area.

<template>
  <vue-clip :on-drop="drop">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      drop () {
        // user stopped dragging the file.
      }
    }

  }
</script>

onTotalProgress(progress, totalBytes, bytesSent)

This event returns the total upload progress for all the files. Think of it as the global progress indicator for multiple files uploaded together.

<template>
  <vue-clip :on-total-progress="totalProgress">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      totalProgress (progress, totalBytes, bytesSent) {
      }
    }

  }
</script>

onQueueComplete

The event is called when all files in the queue have been uploaded to the server.

<template>
  <vue-clip :on-queue-complete="queueCompleted">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      queueCompleted () {
      }
    }

  }
</script>

onMaxFiles

The event is called when maxFiles upload limit has been reached. This event will be fired n timesfor each file exceeding the limit. For example

  • maxFiles - 3
  • filesUploaded - 5
  • eventCalled - 2 times with file instance
<template>
  <vue-clip :on-max-files="maxFilesReached">
  </vue-clip>
</template>

<script>
  export default {

    methods: {
      maxFilesReached (file) {
      }
    }

  }
</script>

File Attributes

The file instance sent along events has following attributes.

Attribute Type Description
name String The client name of the file
status String String The file status, which can be success, error, queued, added.
width Number The file width. Only for images.
height Number The file height. Only for images.
bytesSent Number The total bytes sent to the server so far.
progress Number Total upload progress.
total Number The total number of bytes to be sent to the server.
type String The file mime-type.
size Number The file size on user disk.
dataUrl String File base64 data URL to be used for displaying images preview.
xhrResponse Object Server xhrResponse. Only contains response, responseText and statusCode
errorMessage String Error message when processing a file. If the error is returned from the server, it will be the value of XHR error. Also can be client errors for maxSize etc.
customAttributes Object Each file needs some custom attributes, for example server id to be used for deleting the files.

Adding/Accessing Custom Attributes

file.addAttribute('id', xhr.response.id)

// access id
file.customAttributes.id

Browser Support

  • Chrome 7+
  • Firefox 4+
  • IE 10+
  • Opera 12+
  • Safari 6+

Things to consider

Make sure you add class dz-message to the uploader action wrapped inside clip-uploader-action slot. This makes your entire action body clickable. There are ways to get around it, but I wanted to keep the API transparent, instead of adding a bunch of DOM elements behind the scenes.

<template slot="clip-uploader-action">
  <div>
    <div class="dz-message"><h2> Click or Drag and Drop files here upload </h2></div>
  </div>
</template>