0

I'm working with the dropzonejs-rails gem and incorporating a dropzone for file management. I have uploading working great, but removal of files is causing me anguish. I can't get the listener to attach so that the ajax will hit my server. My forte is Ruby on Rails, and I have only limited javascript experience, so that's probably the real issue here... but maybe someone can help me solve my [shameful] 2-day struggle.

Dropzone is displaying the previewTemplate correctly in the dropzone once the file is uploaded, and the addRemoveLinks: true is displaying the remove link on the thumbnail preview. My goal is that upon the user clicking a file's remove link, a request should be sent to the server to delete the file. I tried many different approaches from blogs, dropzone FAQ, github issues, etc. The closest I have come to success is this: https://github.com/enyo/dropzone/issues/456.

I tried add an event listener to the .dz-remove button upon successful upload. This listener is intended to hit the server with an ajax request to delete the file. Currently, clicking the remove link on the preview thumbnail only removes the preview thumbnail from the dropzone, but does NOT hit the server with the ajax. Here is the javascript code:

// Listening for Dropzone upload success
// imageDropzone is the camelized version of the dropzone form's id. 
Dropzone.options.imageDropzone = {
  init: function() {
    this.on("success", function(file, response, event) {

      // Loads image ID to the file preview for use w/deletion
      var elem;
      elem = document.createElement('input');
      elem.setAttribute("name", "id");
      elem.setAttribute("value", response["id"]);
      elem.setAttribute("type", "hidden");
      file.previewTemplate.appendChild(elem);

      // Add the listener to the dz-remove class link
      $('.dz-remove').on("click", (function(e) {
        var imageId, imageToken, _this;
        _this = this;
        e.preventDefault();
        e.stopPropagation();
        imageId = $(_this).parent().find('[name="id"]').val();
        console.log(_this);
        console.log(imageId);
        console.log(imageToken);
        $.ajax({
          url: "/remove_image",
          type: "DELETE",
          dataType: "script",
          data: {
            id: imageId
          }
        });
        return false;
      }), false);
    });
  }
};

So, it should find the file info associated with the clicked remove link, and sends that info to the server for the delete request. Anyone successfully gotten this approach (or a better one??) working using Javascript for a Ruby on Rails (Rails 4) application?

Let me know if you want any additional information or code. Thanks!

cjn
  • 1,331
  • 1
  • 16
  • 22

1 Answers1

0

I eventually figured out how to make it hit the Rails server through an ajax request via the "remove file" button that comes with dropzonejs-rails through the addRemoveLinks option (when set to true). (see dropzonejs.com for more info on that) This was accomplished with the help of @simmi simmi's answer here.

So, in case anyone is wondering, here's the code in coffeescript. (Sorry for the switcheroo mid-question. If you need it in JS format, use js2coffee.org to convert back to JS format, or I can post it if I get a request for such.) Since it's in coffeescript, explanation comments will be added in-line using the '#' symbol.

/app/assets/javascripts/upload.js.coffee

  # Note that imageDropzone is the #id of MY dropzone HTML element. Yours may be called myDropzone or otherwise - tailor as needed.
  Dropzone.options.imageDropzone = init: ->
    @on "success", (file, response, event) ->  #triggered by our render JSON's status: 200 "success" response rendered from controller on successful upload.

    # Append the image id as a hidden input
    elem = document.createElement('input')
    elem.setAttribute("name", "image[id]")
    elem.setAttribute("value", response["id"])
    elem.setAttribute("type", "hidden")
    file.previewTemplate.appendChild elem

    # Append the image token as a hidden input.
    # Because images are uploaded as an association (Post model has_many :images), there needs to be a common reference so the Post controller can associate the proper pictures during creation of the Post. 
    elem2 = document.createElement('input')
    elem2.setAttribute("name", "image[token]")
    elem2.setAttribute("value", response["token"])
    elem2.setAttribute("type", "hidden")
    file.previewTemplate.appendChild elem2

    # The "remove file" button's listener needs to be added through "file.previewTemplate" for it to work, but note that "click" does NOT work! 
    # It seemed like the built-in listener gets the "click" call before this one we add here, so it never receives this. 
    # So, I used a "mouseup" listener to have a fighting chance, and it worked. 
    file.previewTemplate.addEventListener "mouseup", (e) ->
      _this = this
      e.preventDefault()
      e.stopPropagation()
      # These next two lines are me grabbing the database reference values I put into the previewTemplate (see above). These are passed through the Ajax call at params. I wanted them to come through hashed like params[:images][:id] & params[:images][:token] for easy use with strong_params.
      imageId = $(_this).parent().find('[name="image[id]"]').val()
      imageToken = $(_this).parent().find('[name="image[token]"]').val()
      $.ajax
        url: "/destroyimage" #This is the route I'm hitting to forward to controller
        type: "DELETE"
        dataType: "script"   #Send as JS
        data:
          image:
            id: imageId
            token: imageToken
      false
    false

Here's the Controller info in case it's helpful:

#app/controllers/images_controller.rb

def create
   @image = current_user.images.build(image_params) # Build through the association

   respond_to do |format|
     if @image.save
       # Send Response: id/token for remove button; status 200 OK for Dropzone success
       format.json { render json: @image, id: @image.id, token: @image.token, :status => 200 }
     else
       # Send Response: status 400 ERROR for Dropzone failure
       format.json { render json: { error: @image.errors.full_messages }, :status => 400 }
     end
   end
 end

 def destroy
   image = current_user.images.where(token: image_params[:token]).where(id: image_params[:id]).take  # Probably a little too redundant on security, but whatever
   respond_to do |format|
     if Image.destroy(image.id)
       format.json { head :ok, :status => 200 }
     else
       format.json { render json: { error: @image.errors.full_messages }, :status => 400 }
     end
   end
 end


 private
 def image_params
   # There's probably a better way to do this, but it worked for me.
   params[:image].present? ? params.require(:image).permit(:id, :filename, :token) : nil
 end

Also, please note that this was made utilizing the Rails CarrierWave gem for file uploading. I'm sure it's quite similar for the other file uploading gems out there (paperclip, dragonfly, etc).

Community
  • 1
  • 1
cjn
  • 1,331
  • 1
  • 16
  • 22