Last active
March 9, 2024 05:14
-
-
Save lazaronixon/dca1b48c241422d6347f4b0c93bec739 to your computer and use it in GitHub Desktop.
Dropzone.js + Stimulus + Active Storage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Controller } from "stimulus" | |
import { DirectUpload } from "@rails/activestorage" | |
import Dropzone from "dropzone" | |
import { getMetaValue, findElement, removeElement, insertAfter } from "helpers" | |
Dropzone.autoDiscover = false | |
export default class extends Controller { | |
static targets = [ "input" ] | |
connect() { | |
this.dropZone = createDropZone(this) | |
this.hideFileInput() | |
this.bindEvents() | |
} | |
// Private | |
hideFileInput() { | |
this.inputTarget.disabled = true | |
this.inputTarget.style.display = "none" | |
} | |
bindEvents() { | |
this.dropZone.on("addedfile", (file) => { | |
setTimeout(() => { file.accepted && createDirectUploadController(this, file).start() }, 500) | |
}) | |
this.dropZone.on("removedfile", (file) => { | |
file.controller && removeElement(file.controller.hiddenInput) | |
}) | |
this.dropZone.on("canceled", (file) => { | |
file.controller && file.controller.xhr.abort() | |
}) | |
this.dropZone.on("processing", (file) => { | |
this.submitButton.disabled = true | |
}) | |
this.dropZone.on("queuecomplete", (file) => { | |
this.submitButton.disabled = false | |
}) | |
} | |
get headers() { return { "X-CSRF-Token": getMetaValue("csrf-token") } } | |
get url() { return this.inputTarget.getAttribute("data-direct-upload-url") } | |
get maxFiles() { return this.data.get("maxFiles") || 1 } | |
get maxFileSize() { return this.data.get("maxFileSize") || 256 } | |
get acceptedFiles() { return this.data.get("acceptedFiles") } | |
get addRemoveLinks() { return this.data.get("addRemoveLinks") || true } | |
get form() { return this.element.closest("form") } | |
get submitButton() { return findElement(this.form, "input[type=submit], button[type=submit]") } | |
} | |
class DirectUploadController { | |
constructor(source, file) { | |
this.directUpload = createDirectUpload(file, source.url, this) | |
this.source = source | |
this.file = file | |
} | |
start() { | |
this.file.controller = this | |
this.hiddenInput = this.createHiddenInput() | |
this.directUpload.create((error, attributes) => { | |
if (error) { | |
removeElement(this.hiddenInput) | |
this.emitDropzoneError(error) | |
} else { | |
this.hiddenInput.value = attributes.signed_id | |
this.emitDropzoneSuccess() | |
} | |
}) | |
} | |
// Private | |
createHiddenInput() { | |
const input = document.createElement("input") | |
input.type = "hidden" | |
input.name = this.source.inputTarget.name | |
insertAfter(input, this.source.inputTarget) | |
return input | |
} | |
directUploadWillStoreFileWithXHR(xhr) { | |
this.bindProgressEvent(xhr) | |
this.emitDropzoneUploading() | |
} | |
bindProgressEvent(xhr) { | |
this.xhr = xhr | |
this.xhr.upload.addEventListener("progress", event => this.uploadRequestDidProgress(event)) | |
} | |
uploadRequestDidProgress(event) { | |
const element = this.source.element | |
const progress = event.loaded / event.total * 100 | |
findElement(this.file.previewTemplate, ".dz-upload").style.width = `${progress}%` | |
} | |
emitDropzoneUploading() { | |
this.file.status = Dropzone.UPLOADING | |
this.source.dropZone.emit("processing", this.file) | |
} | |
emitDropzoneError(error) { | |
this.file.status = Dropzone.ERROR | |
this.source.dropZone.emit("error", this.file, error) | |
this.source.dropZone.emit("complete", this.file) | |
} | |
emitDropzoneSuccess() { | |
this.file.status = Dropzone.SUCCESS | |
this.source.dropZone.emit("success", this.file) | |
this.source.dropZone.emit("complete", this.file) | |
} | |
} | |
// Top level... | |
function createDirectUploadController(source, file) { | |
return new DirectUploadController(source, file) | |
} | |
function createDirectUpload(file, url, controller) { | |
return new DirectUpload(file, url, controller) | |
} | |
function createDropZone(controller) { | |
return new Dropzone(controller.element, { | |
url: controller.url, | |
headers: controller.headers, | |
maxFiles: controller.maxFiles, | |
maxFilesize: controller.maxFileSize, | |
acceptedFiles: controller.acceptedFiles, | |
addRemoveLinks: controller.addRemoveLinks, | |
autoQueue: false | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function getMetaValue(name) { | |
const element = findElement(document.head, `meta[name="${name}"]`) | |
if (element) { | |
return element.getAttribute("content") | |
} | |
} | |
export function findElement(root, selector) { | |
if (typeof root == "string") { | |
selector = root | |
root = document | |
} | |
return root.querySelector(selector) | |
} | |
export function removeElement(el) { | |
if (el && el.parentNode) { | |
el.parentNode.removeChild(el); | |
} | |
} | |
export function insertAfter(el, referenceNode) { | |
return referenceNode.parentNode.insertBefore(el, referenceNode.nextSibling); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="dropzone dropzone-default dz-clickable" data-controller="dropzone" data-dropzone-max-file-size="5" data-dropzone-accepted-files="text/csv"> | |
<%= form.file_field :file, direct_upload: true, data: { target: 'dropzone.input' } %> | |
<div class="dropzone-msg dz-message needsclick"> | |
<h3 class="dropzone-msg-title">Solte o arquivo aqui ou clique para fazer o upload.</h3> | |
<span class="dropzone-msg-desc">Use os templates abaixo para criar o arquivo, o tamanho maximo é de 5 MiB.</span> | |
</div> | |
</div> |
@lohannon I'll give it a try. Thanks.
Does anyone know what is the best way to display the files already uploaded in edit case? Thanks
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@tfolk I also had to overwrite some method signatures to include the token and attachment name like this:
and