tors / jquery-fileupload-rails

jQuery File Upload integrated for Rails
669 stars 254 forks source link

jquery fileupload with nested attributes #75

Open sangamgupta85 opened 9 years ago

sangamgupta85 commented 9 years ago

Hi there

Can someone guide me about uploading files using carrierwave with nested attributes and jquery-fileupload-rails plugin.

Basically my requirement is I have a Package that has many Images. How can I create a package with package details along with adding multiple images with nested form using jquery fileupload plugin.

When I create a package with multiple images using nested form, it takes too much time to process the request specially if images are of bigger size around 6-7 MBs. I am using and passenger and nginx in my production server.

Thanks Sangam Gupta

felixbuenemann commented 9 years ago

jQuery File Upload works asynchronously, which means it cannot work with nested forms. Your best bet is to create the records you want to attach your images to first, so you can associate to them in the controller.

Or you can upload the images to a non-nested route and associate them later. The latter case requires you to send some sort of client generated reference, like a uuid with the record that you can then also submit in your nested form and you would need to have some kind of cleanup task to remove abandoned uploads.

Oh and another variation would be to submit your nested form using ajax, add your file upload outside that form and trigger the upload using the jquery fileupload js api, if your form submit was successful.

sangamgupta85 commented 9 years ago

Thanks for suggestions. Could you please let me know which option would be the best for my current implementation using jquery-fileupload-rails plugin.

         <%= nested_form_for @package , :html => {multipart: true} do |f| %>
              <%= f.fields_for :pictures do |p| %>
                  <%= p.file_field :image %>
                  <%= p.link_to_remove "Remove this image" %>
              <%end%>
            <%= f.link_to_add "Add image" %>

            <%=f.submit "Submit" %>
         <%end%>

Any example with code snippet would be great.

ssilvius commented 9 years ago

So this can be done... with some twisting.

I changed my object names to make them more generic and easier to understand. Parent is the model that you'll be nesting into. Link is the child object.

The Form (haml):

    = form_for @parent, :remote => true do |f|
      = f.fields_for :links do |img|
        .form-group.hidden
          = img.file_field :image
        .target
          #dropzone.fade drop files here  
        .progress-wrapper
          %p 
            Bitrate: 
            %span.bitrate
          .progress
            .progress-bar{:role => "progressbar"}
              0%

In the parent controller you need to setup the params, add

links_attributes: [:image]

to the param.require(:parent).permit()

Also in the parent controller you need to build an "link" object so it's ready in the view.

def new
  @parent = Parent.new
  @parent.links.build
end

In the parent model

has_many :links,   :dependent => :destroy 
accepts_nested_attributes_for :links

and of course add to the links model

belongs_to :parent

and the javascript, really standard

$(document).ready(function() {
    var multiple_photos_form = $('.edit_parent');
    var wrapper = multiple_photos_form.find('.progress-wrapper');
    var bitrate = wrapper.find('.bitrate');
    var progress_bar = wrapper.find('.progress-bar');

    multiple_photos_form.fileupload({
      dataType: 'script',
      dropZone: $('#dropzone'),
      add: function (e, data) {
        types = /(\.|\/)(gif|jpe?g|png|mov|mpeg|mpeg4|avi)$/i;
        file = data.files[0];
        if (types.test(file.type) || types.test(file.name)) {
          data.submit();
        }
        else { alert(file.name + " is not a image or movie file."); }
      }
    });
    multiple_photos_form.on('fileuploadstart', function() {
      wrapper.show();
    });
    multiple_photos_form.on('fileuploaddone', function() {
      wrapper.hide();
      progress_bar.width(0);
    });

    multiple_photos_form.on('fileuploadprogressall', function (e, data) {
      bitrate.text((data.bitrate / 1024).toFixed(2) + 'Kb/s');
      var progress = parseInt(data.loaded / data.total * 100, 10);
      progress_bar.css('width', progress + '%').text(progress + '%');
    });
    $(document).bind('dragover', function (e) {
      var dropZone = $('#dropzone'),
              timeout = window.dropZoneTimeout;
      if (!timeout) {
        dropZone.addClass('in');
      } else {
        clearTimeout(timeout);
      }
      var found = false,
              node = e.target;
      do {
        if (node === dropZone[0]) {
          found = true;
          break;
        }
        node = node.parentNode;
      } while (node != null);
      if (found) {
        dropZone.addClass('hover');
      } else {
        dropZone.removeClass('hover');
      }
      window.dropZoneTimeout = setTimeout(function () {
        window.dropZoneTimeout = null;
        dropZone.removeClass('in hover');
      }, 100);
    });
});

Hope this helps.

sangamgupta85 commented 9 years ago

@ssilvius thanks for the code snippet but it seems it's not working as expected.

Here is repo - https://github.com/sangamgupta85/file-upload

Could you please advise?