Skip to content

Instantly share code, notes, and snippets.

@dpaluy
Last active July 20, 2023 01:25
Show Gist options
  • Save dpaluy/5d2115ec3a01cd10fec748d4d803dc81 to your computer and use it in GitHub Desktop.
Save dpaluy/5d2115ec3a01cd10fec748d4d803dc81 to your computer and use it in GitHub Desktop.
Trix and Shrine for WYSIWYG

How to use Trix and Shrine for WYSIWYG Editing with Drag-and-Drop Image Uploading

Building It

Install and Configure Shrine and Trix Dependencies

  1. Setup Shrine:

First, add the Shrine gem to your application's Gemfile:

 gem 'shrine'

Then, create a file at config/initializers/shrine.rb within your app for setting up Shrine's options. Here we'll use local filesystem storage for uploaded files, but in a production environment using S3 or another cloud storage is a great option:

 require 'shrine'
 require 'shrine/storage/file_system'

 Shrine.storages = {
   # temporary storage
   cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'),

   # permanent storage
   store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/store'),
 }

 Shrine.plugin :activerecord
 Shrine.plugin :cached_attachment_data # for forms

Lastly, create an uploaded class in app/uploaders/photo_uploader.rb:

   class PhotoUploader < Shrine
   end
  1. Setup Trix:

Add Trix to your Gemfile:

   gem 'trix'

Add the JS and CSS requires to your manifest files:

app/assets/stylesheets/application.css:

 *= require trix

app/assets/javascripts/application.js:

 //= require trix

For any form fields you'd like add WYSIWYG editing to, use the trix_editor form helper:

 f.trix_editor :body

Create an Image Resource for Uploads and an Associated Controller

  1. Next, let's create a general Image model that will represent our uploaded images:

Run: rails generate model image image_data:text

Within app/models/image.rb add the following line to set it up for Shrine uploads:

 # adds an `image` virtual attribute
 include ::PhotoUploader::Attachment.new(:image)
  1. For our purposes, we only need to have one controller to handle the image upload actions. We're only using this new controller for storing the uploaded images and then returning the url of the saved file. Trix will take it from there.

Create a new controller at app/controllers/images_controller.rb with the following code:

class ImagesController < ApplicationController respond_to :json

 def create
   image_params[:image].open if image_params[:image].tempfile.closed?

   @image = Image.new(image_params)

   respond_to do |format|
     if @image.save
       format.json { render json: { url: @image.image_url }, status: :ok }
     else
       format.json { render json: @image.errors, status: :unprocessable_entity }
     end
   end
 end

 private

 def image_params
   params.require(:image).permit(:image)
 end

end

This controller sets up a basic create action that we can POST to via JavaScript. If the image saving is successful, it then returns back JSON with the destination URL of the saved image. We'll use this returned JSON later in our JavaScript to send the file's location to Trix.

  1. Lastly, to finish setting up our image uploading we need to add a route where we can POST to this action. Add the following to:

config/routes.rb

 resources :images, only: [:create]

Add JavaScript Handling for Trix:

When a file is dragged and dropped onto the Trix editor, it fires a JavaScript event with the file information and content. Now that we have all of the necessary Rails model and controller setup in place, the final step to wire everything together is to add JavaScript to handle this attachment event and upload the file to our server.

  1. Create the following file:

app/assets/javascripts/trix_attachments.js

 $(document).ready(function() {
   Trix.config.attachments.preview.caption = {
     name: false,
     size: false
   };

   function uploadAttachment(attachment) {
     var csrfToken = $('meta[name="csrf-token"]').attr('content');
     var file = attachment.file;
     var form = new FormData;
     var endpoint = "/images";
     form.append("Content-Type", file.type);
     form.append("image[image]", file);

     xhr = new XMLHttpRequest;
     xhr.open("POST", endpoint, true);
     xhr.setRequestHeader("X-CSRF-Token", csrfToken);

     xhr.upload.onprogress = function(event) {
       var progress = event.loaded / event.total * 100;
       return attachment.setUploadProgress(progress);
     };

     xhr.onload = function() {
       if (this.status >= 200 && this.status < 300) {
         var data = JSON.parse(this.responseText);
         return attachment.setAttributes({
           url: data.url,
           href: data.url
         });
       }
     };

     return xhr.send(form);
   };

   document.addEventListener("trix-attachment-add", function(event) {
     var attachment = event.attachment;
     if (attachment.file) {
       return uploadAttachment(attachment);
     }
   });
 });

Our JavaScript attaches to Trix's trix-attachment-add event that gets called whenever a file is dropped onto the editor. We then take that file and use an XMLHttpRequest to upload it to the server asynchronously. If the server returns a successful status, we parse the URL of the uploaded file from the response's JSON and return it back for Trix to use.

  1. Now, include this new JavaScript file in your main manifest file:

app/assets/javascripts/application.js:

     //= require trix_attachments

See It All in Action

Spin up your server if you haven't already, and then browse to the page where you added the Trix editor tag. Dropping an image onto the editor will POST to your server, create an Image resource with the attached image file, and then send the updated URL back to Trix. When you save the Trix-enabled form, the uploaded Image's URL will then be saved within the string content of that field. Now the tools for dynamically editing content can be in your client's hands!

Other Resources

Original article: https://www.headway.io/blog/how-to-use-trix-and-shrine-for-wysiwyg-editing-with-drag-and-drop-image-uploading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment