javascriptruby-on-railswysiwygbasecamp

Basecamp's Trix WYSIWYG editor gem not saving file attachment in Rails 4 app


I added the Trix editor gem to my Rails 4 app and followed the instructions on that page exactly. The text is displaying correctly after I save a post (bold/italic/strikethrough/paragraph spacing all good), BUT any images that I drag into the text editor disappear when I save the post. What am I doing wrong?

Thanks.

app/assets/stylesheets/application.scss:

/*
    *= require_self
    *= require trix 
*/

@import "bootstrap";
@import "bootstrap-sprockets";
@import "font-awesome"; 
@import url(https://fonts.googleapis.com/css?family=Delius+Swash+Caps);
@import url(https://fonts.googleapis.com/css?family=Reenie+Beanie);
@import url(https://fonts.googleapis.com/css?family=Special+Elite);
@import url(https://fonts.googleapis.com/css?family=Londrina+Shadow);

app/assets/javascripts/application.js:

//= require jquery
//= require jquery_ujs
//= require bootstrap-sprockets
//= require bootstrap
//= require trix
//= require_tree .

_form.html.erb:

<%= form_for @article, html: {multipart: true} do |f| %>
  <p>
    <%= f.label :image %>
    <%= f.file_field :image %>
  </p>
  <p>
    <%= f.label :title %>
    <%= f.text_field :title %>
  </p>
  <p>
    <%= f.label :subtitle %>
    <%= f.text_field :subtitle %>
  </p>

  <%= f.label :text %>
  <%= f.trix_editor :text, class: 'trix-content' %>

  <p>
    <%= f.label :tags %>
    <%= f.text_field :tag_list %>
  </p>

  <p>
    <%= f.submit %>
  </p>

<% end %>

new.html.erb

<div class="container"> 
    <div class="jumbotron">
        <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1>New article</h1>
                <%= render 'form' %>
                <%= link_to 'Back', articles_path %>
            </div>
        </div>
    </div>
</div>

Gemfile:

gem 'rails', '4.2.2'
gem 'pg'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'jquery-rails'
gem 'bootstrap-sass', '~> 3.3', '>= 3.3.6'
gem 'devise', '~> 3.5', '>= 3.5.6'
gem "font-awesome-rails"
gem 'paperclip', '~> 4.2'
gem 'aws-sdk', '~> 1.66'
gem 'figaro', '~> 1.1', '>= 1.1.1'
gem 'simple_form'
gem 'mail_form'
gem 'acts-as-taggable-on', '~> 3.4'
gem 'fog'
gem 'rmagick', '~> 2.15', '>= 2.15.4'
gem 'carrierwave'
gem "fog-aws"
gem 'trix', '~> 0.9.0'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0', group: :doc

Solution

  • In this example, I've created a Image model and controller that has a GET and POST method to them. I used refile to handle the file uploads, but it would work similar with carrierwave. The idea is to listen on the event trix-attachment-add and send an XHR POST to your ImagesController. From here, you can send a JSON response back to your XHR Request and call JSON.parse to capture the response text. Trix expects a valid URL to be returned and the attributes set on the attachment. Until this is properly done, it will be in a pending state and not actually save the image and/or caption.

      # ImagesController
      def create
        @image = Image.new(image_params)
        @image.save
    
        respond_to do |format|
          format.json { render :json => { url: Refile.attachment_url(@image, :image)} }
        end
      end
    
    # Some Javascript
    
    (function() {
      var host, uploadAttachment;
    
      document.addEventListener("trix-attachment-add", function(event) {
        var attachment;
        attachment = event.attachment;
        if (attachment.file) {
          return uploadAttachment(attachment);
        }
      });
    
      host = "/images";
    
      uploadAttachment = function(attachment) {
        var file, form, xhr;
        file = attachment.file;
        form = new FormData;
        form.append("Content-Type", file.type);
        form.append("image[image]", file);
        xhr = new XMLHttpRequest;
        xhr.open("POST", host, true);
        xhr.upload.onprogress = function(event) {
          var progress;
          progress = event.loaded / event.total * 100;
          return attachment.setUploadProgress(progress);
        };
        xhr.onload = function() {
          var href, url;
            url = href = JSON.parse(this.responseText).url;
            return attachment.setAttributes({
              url: url,
              href: href
            });
        };
        return xhr.send(form);
      };
    
    
    }).call(this);