Skip to content

Instantly share code, notes, and snippets.

@t27duck
Created November 3, 2021 23:50
Show Gist options
  • Save t27duck/a8f8d9741b4fb32f4432d777330765bf to your computer and use it in GitHub Desktop.
Save t27duck/a8f8d9741b4fb32f4432d777330765bf to your computer and use it in GitHub Desktop.
import { Controller } from "@hotwired/stimulus"
import { DirectUpload } from "@rails/activestorage"
// This is a very rough hack to allow a file upload field that we'd like to be a direct upload
// but is loading into the dom in a way that the normal form submit event is clobbered by other
// processes (like hotwire).
//
// This is made based off of the ActiveStoreage guides for integrating direct uploads for other
// JS frameworks. The biggest difference here is when the user picks a file, the file is immedidatly
// uploaded to the sever. More things in this controller should be fleshed out in the future such
// as erorr handling and a progress bar.
//
// This controller should be attached to a file upload form element and not the main form tag. It is
// probably ill-advised to mix this with a regular pre-rendered form that also does direct uploads.
export default class extends Controller {
connect() {
this.element.addEventListener("change", this.handleFileUploadEvent.bind(this))
}
disconnect() {
this.element.removeEventListener("change", this.handleFileUploadEvent)
}
handleFileUploadEvent(event) {
Array.from(this.element.files).forEach(file => this.uploadFile(file))
}
uploadFile(file) {
// The field needs the file_field direct_upload: true, which
// provides data-direct-upload-url
const url = this.element.dataset.directUploadUrl
const upload = new DirectUpload(file, url)
upload.create((error, blob) => {
if (error) {
// TODO: Handle the error better?
console.error("Failed to upload file", error)
} else {
// Add an appropriately-named hidden input to the form with a
// value of blob.signed_id so that the blob ids will be
// transmitted in the normal upload flow
//
// This takes advantage of the fact that the form will only submit
// the input created last in the DOM tree, ignoring the original
// upload input.
const hiddenField = document.createElement("input")
hiddenField.setAttribute("type", "hidden")
hiddenField.setAttribute("value", blob.signed_id)
hiddenField.name = this.element.name
this.element.closest("form").appendChild(hiddenField)
const result = document.createElement("div")
result.innerHTML = "Upload complete"
this.element.after(result)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment