Skip to content

Instantly share code, notes, and snippets.

@zealot128
Last active March 13, 2024 04:57
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save zealot128/3f75046ea7efc4489f03e4b71244d0f8 to your computer and use it in GitHub Desktop.
Save zealot128/3f75046ea7efc4489f03e4b71244d0f8 to your computer and use it in GitHub Desktop.

Active Storage - Vue Direct Upload Example

Use component like this (just written down here with slots, not deeply tested):

<direct-upload :upload-url='..' :multiple='true' :label='Drop files into this area to start upload' @upload='uploadCallback'>
  <!-- <slot></slot>  Edit default text in drop area --->
  <slot name='progress' slot-scope="slotProps">
    <span> {{ slotProps.file.filename }} {{slotProps.progress }} </span>
  <slot>
</>

<script>

export default {
...
  methods: {
    uploadCallback({ file, blob }) {
      const urlToView = "/rails/active_storage/blobs/" + blob.signed_id + "/" + blob.filename
      // To something with it, add to hidden field etc.
    }
  }
 }

how to get uploadUrl ?

Pass result from rails_direct_uploads_url from Rails view/controller down into JS via JSON attributes etc:

my-upload-form(upload-url="#{rails_direct_uploads_url}")
<template lang='pug'>
div.drop-zone(:class='{dragging: isDragging }'
@dragover.prevent='dragover'
@dragenter.prevent='dragover'
@drop.prevent.stop='onDrop'
@dragleave.prevent='dragleave')
div(:class='{ hidden: uploadInProgress }' @click='openFileBrowser')
slot
i {{label}}
input(type='file' :multiple='multiple' ref='input' style='display: none')
.list-group(v-if='uploads.length > 0')
.list-group-item(v-for='file in uploads')
slot(name='progress' v-bind:file='file' v-bind:progress='progress')
i.mdi.mdi-loading.fa-spin
i Uploading: {{file.file.filename }}
br
b-progress(:value="progress" :max="100" :animated='true' style='max-width: 200px; height: 2px')
</template>
<script>
import { DirectUpload } from 'activestorage'
import bProgress from 'bootstrap-vue/es/components/progress/progress';
export default {
components: {
bProgress
},
props: {
uploadUrl: {
type: String,
required: true
},
multiple: {
type: Boolean,
default: true
},
label: {
type: String,
default: "Schieben Sie das Dokument hier rein oder klicken Sie hier um hochzuladen"
}
},
data() {
return {
isDragging: false,
progress: 0,
uploads: [],
}
},
mounted() {
this.input.addEventListener('change', (_event) => {
Array.from(this.input.files).forEach(file => this.upload(file))
this.input.value = null
})
},
methods: {
// callbacks from active storage
directUploadWillStoreFileWithXHR(xhr) {
xhr.upload.addEventListener("progress", event => this.onProgress(event))
},
directUploadWillStoreFileWithXHR(xhr) {
this.lastXhr = xhr
},
dragover() {
this.isDragging = true
},
dragleave() {
this.isDragging = false
},
onDrop(event) {
this.dragleave();
const files = event.dataTransfer.files;
Array.from(files).forEach(file => this.upload(file))
},
onProgress(event) {
this.progress = (event.loaded / event.total) * 100
},
upload(file) {
const upload = new DirectUpload(file, this.uploadUrl, this)
this.uploads.push({ file, upload })
this.progress = 0
upload.create((error, blob) => {
if (error) {
// TODO
} else {
this.uploads = this.uploads.filter(payload => payload.file.filename !== file.filename)
this.$emit('upload', { file, blob })
}
})
},
openFileBrowser() {
this.input.click()
}
},
computed: {
uploadInProgress() { return this.uploads.length > 0 },
input() {
return this.$refs.input;
}
}
}
</script>
<style lang='scss' scoped>
.drop-zone {
border: 3px solid #eee;
min-height: 50px;
}
.drop-zone.dragging {
border: 3px dashed #444;
position: relative;
}
.drop-zone.dragging:before {
content: "Lassen Sie die Datei hier fallen um den Upload zu beginnen";
display: block;
position: absolute;
top: 0;
right: 5px;
font-style: italic;
color: #555;
}
input[type=file][data-direct-upload-url][disabled] {
display: none;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment