title: RoR: using Dropzone.js with jQuery sortable
body: <p>I have Dropzone Js combined with paperclip in my rails app, I have also managed to apply jquery's sortable to it. </p>
  
  <p>my objective is: </p>
  
  <ol>
  <li>to be able to save every image's position to the db on
  upload success  </li>
  <li>update the position for each image in the db when I change (sort) the order of the images.</li>
  </ol>
  
  <p>my upload model has :image and :position
  and my routes has collection {post "sort"}</p>
  
  <p>my controller code:</p>
  
  <pre><code>class UploadsController &lt; ApplicationController
  
    def new
      @upload = Upload.new
    end
  
    def upload_list
      @uploads = Upload.all.select(:id, :image_file_name, :image_file_size).order("position")
  
      respond_to do |format|
        format.json {render json: @uploads.to_json(methods: [:path])}
      end
    end
  
    def create
      @upload = Upload.create(upload_params)
      @upload.position = params[:upload][:position]
  
      if @upload.save
        render json: { message: "success", fileID: @upload.id }, :status =&gt; 200
      else
        render json: { error: @upload.errors.full_messages.join(',')}, :status =&gt; 400
      end
    end
  
    def sort
      //params[:upload] gives undefined method for nil error 
      Upload.all.each_with_index do |id, index|
        Upload.where(id: id).update_all({position: index+1})
      end
      render nothing: true
    end
  
    def destroy
      @upload = Upload.find(params[:id])
      if @upload.destroy
        render json: { message: "File deleted from server" }
      else
        render json: { message: @upload.errors.full_messages.join(',') }
      end
    end
  
    private
      def upload_params
        params.require(:upload).permit(:image, :position)
      end
  end  
  </code></pre>
  
  <p>upload.rb:</p>
  
  <pre><code>class Upload &lt; ActiveRecord::Base
  
    has_attached_file :image, :styles =&gt; { :medium =&gt; "300x300&gt;",:thumb =&gt; "100x100&gt;" }
  
    validates_attachment :image,
                     :presence =&gt; true,
                     :content_type =&gt; { :content_type =&gt; /\Aimage\/.*\Z/ },
                     :size =&gt; { :less_than =&gt; 1.megabyte } 
  
    def path
      image.url
    end
  
    def as_json(options = { })
      h = super(options)
      h["name"] = h.delete "image_file_name"
      h["size"] = h.delete "image_file_size"
      h
    end
  
  end 
  </code></pre>
  
  <p>new.html.erb:</p>
  
  <pre><code>&lt;h1&gt;Uploads#new&lt;/h1&gt;
  
  &lt;%= form_for(@upload, html: { multipart: true, class: "dropzone"}) do |f| %&gt;
    &lt;div class="fallback"&gt;
      &lt;%= f.file_field :image %&gt;
      &lt;%= f.submit "Upload" %&gt;
    &lt;/div&gt;
  &lt;% end %&gt;  
  </code></pre>
  
  <p>and finally uploads.js:</p>
  
  <pre><code>$(document).ready(function(){
  
    Dropzone.autoDiscover = false;
  
    $("#new_upload").dropzone({
      maxFilesize: 1,
      paramName: "upload[image]",
      addRemoveLinks: true,
      dictRemoveFile: "Delete",
  
      //to show existing images from db 
      init: function() {
        var thisDropZone = this;
        $.getJSON('upload_list', function(data) {
          $.each(data, function(index, val) {
            var mockFile = { name: val.name, size: val.size };
            thisDropZone.emit("addedfile", mockFile);
            thisDropZone.emit("thumbnail", mockFile, val.path);
            $(mockFile.previewTemplate).find('.dz-remove').attr('id', val.id);
  
            // adding id attribute for serialize
            $(".dz-preview:last-child").attr('id', "image_" + val.id);
          });
        });
      },
  
      success: function(file, response){
        $(file.previewTemplate).find('.dz-remove').attr('id', response.fileID);
        $(file.previewElement).addClass("dz-success");
  
        var order = $('.dropzone').sortable('serialize');
        $.ajax({
          type: 'POST',
          url: '/uploads/sort',
          data: order,
          success: function(data){
            console.log(data);
          }        
        });
      },
  
      removedfile: function(file){
        var id = $(file.previewTemplate).find('.dz-remove').attr('id');
        file.previewElement.remove();
  
        $.ajax({
          type: 'DELETE',
          url: '/uploads/' + id,
          success: function(data){
            console.log(data.message);
          }
        });
  
        var order = $('.dropzone').sortable('serialize');
        $.ajax({
          type: 'POST',
          url: '/uploads/sort',
          data: order,
          success: function(data){
            console.log(data);
          }        
        });
      }
    });
  
   //this function is for sorting + updating positions of old images loaded by the init function.
    $(function() {
      $(".dropzone").sortable({
        items:'.dz-preview',
        cursor: 'move',
        opacity: 0.5,
        containment: '.dropzone',
        distance: 20,
        update: function(event, ui) {
          var order = $('.dropzone').sortable('serialize');
          $.ajax({
            type: 'POST',
            url: '/uploads/sort',
            data: order,
            success: function(data){
              console.log(data);
            }        
          });
        }
      });
      $(".dropzone").disableSelection();
    });
  }); 
  </code></pre>
  
  <p>I know I got lots of cleaning up to do but I'm trying to get it to work first.</p>
  
  <p>I understand that I need an id preceded by an "_" in each image's .dz-preview of dropzone for serialize to work. However the ajax post in the success function is working fine without applying any id attribute, so when I upload 3 images, in the db the positions are 1, 2 and 3 respectively, also the removedfile function works, if i remove the second image, image1 gets position "1" and image3 gets the position attribute updated to 2, thanks to the sort method in the controller.</p>
  
  <p>what I tried is add a line in the init function to append the id attribute to .dz-preview  and refresh the page so that i test the init function and tried the sorting again, but its the same, what I get is:</p>
  
  <pre><code>Started POST "/uploads/sort" for 127.0.0.1 at 2015-01-12 12:47:39 +0200
  Processing by UploadsController#sort as */*
  Parameters: {"image"=&gt;["318", "320", "319"]}
  Upload Load (2.7ms)  SELECT "uploads".* FROM "uploads"
  SQL (5.4ms)  UPDATE "uploads" SET "position" = 1 WHERE "uploads"."id" = 318
  SQL (14.7ms)  UPDATE "uploads" SET "position" = 2 WHERE "uploads"."id" = 319
  SQL (1.8ms)  UPDATE "uploads" SET "position" = 3 WHERE "uploads"."id" = 320
  Rendered text template (0.1ms)
  Completed 200 OK in 54ms (Views: 2.0ms | ActiveRecord: 29.6ms)
  </code></pre>
  
  <p>notice the order of the parameters so image "318" should take position 1, "320" should take position 2 and "319" should take position 3...but as shown the new order is ignored from the update...</p>
  
  <p>any ideas what I'm missing? is it the sort method or the .sortable function?</p>
  
  <p>Thanks</p>