This articles was originally published on Rails Designer
In a previous article I explored a way to preview images before upload with Stimulus.
I know want to extend its functionality by adding a drag & drop. Along the way I am also using Stimulus outlets to tie the two functionalities together. Showcasing more advanced use of small Stimulus controllers.
I assume you walked through all the steps of the previous mentioned article.
Let's start with the HTML. It's using the other HTML with just a few attributes added.
<div data-controller="image-preview dropzone" data-action="dragover->dropzone#dragOver dragleave->dropzone#dragLeave drop->dropzone#drop">
  <img data-image-preview-target="canvas" hidden class="object-cover size-48">
  <input type="file" accept="image/*" data-image-preview-target="source" data-dropzone-target="input" data-action="image-preview#show" hidden>
</div>
Let's create the dropzone_controller.js.
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
  static targets = ["input"];
  dragOver(event) {
    event.preventDefault();
  }
  dragLeave(event) {
    event.preventDefault();
  }
  drop(event) {
    event.preventDefault();
    this.#updateInputField(event.dataTransfer.files[0]);
  }
}
All these methods do is preventing the default event when these actions are invoked. The drop() function also calls the private function this.#updateInputField(). Let's add it.
export default class extends Controller {
  // …
  // private
  #updateInputField(file) {
    const dataTransfer = new DataTransfer();
    dataTransfer.items.add(file);
    this.inputTarget.files = dataTransfer.files;
  }
  // …
}
This will inject the dropped image into the inputTarget field. And just like that you can drag and drop images onto the element. 🤯
Preview images with outlets
An important part is missing though… the image isn't show which looks like a bug. Luckily with the image_preview_controller.js already done. This is a simple exercise.
First tweak the HTML by adding the following attributes:
<div data-controller="image-preview dropzone" data-dropzone-image-preview-outlet="#image-preview" data-action="dragover->dropzone#dragOver dragleave->dropzone#dragLeave drop->dropzone#drop" id="image-preview>
  <img data-image-preview-target="canvas" hidden class="object-cover size-48">
  <input type="file" accept="image/*" data-image-preview-target="source" data-dropzone-target="input" data-action="image-preview#show" hidden>
</div>
Added:
- 
data-dropzone-image-preview-outlet="#image-preview";
- 
id="image-preview".
Now two lines are needed in the dropzone_controller.js.
export default class extends Controller {
  static outlets = ["image-preview"];
  // …
  drop(event) {
    // …
    this.imagePreviewOutlet.show();
    // …
  }
  // …
}
Now when you drop an image it fires the show() function on the image_preview_controller.js. 🥳
I like to use small Stimulus controllers like these that work great together.
Rails Designer has this Stimulus controller packaged with a few extras added. Get your copy today.
 
 
              
 
    
Top comments (0)