Rails jquery-file-upload: multiple file uploads in nested file form in create form results in a separate element created for each upload

I was trying to implement multiple file upload in build form in Rails using Jquery-file-upload and Carrierwave. It currently works with both a single file and multiple files, provided that all multiple files were made in the same form. If the user tries to upload multiple files in multiple choices and then clicks the submit button, multiple destinations are created instead, with each one getting a separate highlighted file (if there are multiple files, if that choice has more than one file). So I can create a new element and attach A to it and it will work, or I can create a new element and attach B, C and D right away and all three will be attached when the element is created. However, if I attach A,and then before submitting the create form, I decided to concatenate B and C at the same time, I will have two new items with the same fields except for their IDs and their attachments, where the first will have A and the second will have B and C.

All the examples I found for uploading multiple files in create form do not address this specific issue.

Here is my current code. The creation form is for Assignments, which are in the form of a file for receipts (images to be linked to Assignments).

Assignment model:

class Assignment < ActiveRecord::Base
  belongs_to :site
  belongs_to :user
  attr_accessible :receipts, :assigned_by, :assigned_to, :deadline, :hours, :name, :notes, :phase_number, :project_number, :ready, :site_id, :status, :support_staff, :category, :receipts_attributes 
  has_many :receipts
  accepts_nested_attributes_for :receipts, :reject_if => :all_blank, :allow_destroy => true
  after_save :determine_status
  has_paper_trail :on => [:update, :destroy]


  def task_and_id
    self.category + ' ' + id.to_s
  end

  private
    def determine_status
      if self.status == "Unassigned" && !self.assigned_to.blank?
        self.update_column :status, 'Assignment Pending'
      end
    end
end

      

Assignment controller (including creation method only)

def create
    puts params
    @assignment = Assignment.new(params[:assignment])
    @assignment.status = "Assignment Pending"
    @assignment.ready = true
    if current_user
       @assignment.assigned_by = current_user.id
    end

    respond_to do |format|
      if @assignment.save
        user = User.find(@assignment.assigned_to)
        if params[:receipts]
            params[:receipts].each do |receipt|
                @assignment.receipts.create(receipt: receipt)
            end
        end
        UserMailer.other_task_confirmation(user, @assignment).deliver
        if request.xhr? # if its a ajax request then redirect with javascript
            flash[:notice] = 'Your ' + @assignment.category + ' Task has been created.'
            flash.keep(:notice)
        end
        format.html { redirect_to @assignment, notice: 'Your ' + @assignment.category + ' Task has been created.' }
        format.json { render json: @assignment, status: :created, location: @assignment }
      else
        format.html { render action: "new" }
        format.json { render json: @assignment.errors, status: :unprocessable_entity }
      end
    end
end

      

Type of appointment form

<%= form_for(@assignment, :url => {:action => 'create'}, :html => { :class => 'form-horizontal', :id => 'assignment-form', :multipart => true}) do |f| %>
  <% if @assignment.errors.any? %>
      <% @assignment.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
  <% end %>

  <div class="form-inputs">


    <div class="control-group">
    <%= f.label :assigned_by, :class => 'control-label' %>
      <div class="controls">
          <input type="text" value="<%= current_user.name %>" readonly>
          <%= f.text_field :assigned_by, :value => current_user.id, :style => "display: none" %>
      </div>
    </div>
     <div class="control-group">
    <%= f.label :'Task Name', :class => 'control-label' %>
      <div class="controls">
          <%= f.text_field :name %>
      </div>
    </div>
    <div class="control-group">
    <%= f.label :'Task Type', :class => 'control-label' %>
      <div class="controls">
          <%= f.select :category, options_for_select(tasktype), { :include_blank => "" } %>
      </div>
    </div>
    <div class="control-group">
    <%= f.label :assigned_to, :class => 'control-label' %>
      <div class="controls">
          <%= f.select :assigned_to, options_for_select(User.where(:is_active => 1).order("name").map{|user| [user.name, user.id]}, @assignment.assigned_to), { :include_blank => "" } %>
      </div>
    </div>

    <div class="control-group">
      <%= f.label 'Project*', :class => 'control-label' %>
      <div class="controls">
        <%= f.collection_select :project_number, Project.where(:is_active => 1).order(:project_number), :project_number, :project_number, :include_blank => true %>
      </div>
    </div>
    <div class="control-group phase-task">
      <%= f.label 'Phase & Task*', :class => 'control-label' %>
      <div class="controls">
        <%= f.grouped_collection_select :phase_number, Project.where(:is_active => 1).order(:project_number), :phases, :project_number, :id, :phase_and_task, :include_blank => true %>
      </div>
    </div>

    <div class="control-group">
    <%= f.label :deadline, "Deadline", :class => 'control-label' %>
      <div class="controls">
          <%= f.text_field :deadline, :class => 'date_picker' %>
      </div>
    </div>
    <div class="control-group">
    <%= f.label :hours, "Hours", :class => 'control-label' %>
      <div class="controls">
          <%= f.number_field :hours, :step => 'any' %>
      </div>
    </div>

    <div class="control-group">
    <%= f.label :notes, "Notes", :class => 'control-label' %>
      <div class="controls">
          <%= f.text_area :notes, :class => 'span6', :rows => '8' %>
      </div>
    </div>

    <h4 class="controls">Select Attachments</h4>

    <div id="receipt-list"></div>



      <div class="container" id="fileupload">
    <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
    <div class="row fileupload-buttonbar">
      <div class="span7">
        <!-- The fileinput-button span is used to style the file input field as button -->
          <%= f.file_field :image, multiple: true, :id => 'uploadbutton', :name => 'assignment[receipts_attributes][][image]' %>
        <button type="submit" class="btn btn-primary start" id="upload-everything" style="display: none">
          <i class="icon-upload icon-white"></i>
          <span>Start upload</span>
        </button>
        <button type="reset" class="btn btn-warning cancel">
          <span>Remove all</span>
        </button>
        <button type="button" class="btn btn-danger delete" style="display: none">
          <i class="icon-trash icon-white"></i>
          <span>Delete</span>
        </button>
        <input type="checkbox" class="toggle" style="display: none">
      </div>
      <div class="span5">
        <!-- The global progress bar -->
        <div class="progress progress-success progress-striped active fade">
          <div class="bar" style="width:0%;"></div>
        </div>
      </div>
    </div>
    <!-- The loading indicator is shown during image processing -->
    <div class="fileupload-loading"></div>
    <br>
    <!-- The table listing the files available for upload/download -->
    <table class="table table-striped" id='file-table'><tbody class="files" data-toggle="modal-gallery" data-target="#modal-gallery"></tbody>
    </table>
        </div>

</div>
    <div class="form-actions">
    <button type="button" class="btn" id="upload-all">submit</button>
    <%= f.button :submit, :class => 'btn', :style => "display: none;", :id => "submit-button" %>
  </div>

  <button type="button" class="btn" id="redirect-button" style="display: none;"></button>
  <button type="button" class="btn" id="redirect-button2" style="display: none;"></button>
<script>
  var fileUploadErrors = {
  maxFileSize: 'File is too big',
  minFileSize: 'File is too small',
  acceptFileTypes: 'Filetype not allowed',
  maxNumberOfFiles: 'Max number of files exceeded',
  uploadedBytes: 'Uploaded bytes exceed file size',
  // emptyResult: 'Empty file upload result'
  };
</script>

<!-- The template to display files available for upload -->
<script id="template-upload" type="text/x-tmpl">
  {% for (var i=0, file; file=o.files[i]; i++) { %}
  <tr class="template-upload fade">
    <td class="preview"><span class="fade"></span></td>
    <td class="name"><span>{%=file.name%}</span></td>
    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
    {% if (file.error) { %}
    <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
    {% } else if (o.files.valid && !i) { %}
    <td>
      <div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
    </td>
    <td class="start">{% if (!o.options.autoUpload) { %}
      <button class="btn btn-primary" style="display: none">
        <i class="icon-upload icon-white"></i>
        <span>{%=locale.fileupload.start%}</span>
      </button>
      {% } %}</td>
    {% } else { %}
    <td colspan="2"></td>
    {% } %}
    <td class="remove">{% if (!i) { %}
      <button class="btn btn-warning">
        <span>{%=locale.fileupload.cancel%}</span>
      </button>
      {% } %}</td>
  </tr>
  {% } %}
</script>
<!-- The template to display files available for download -->
<script id="template-download" type="text/x-tmpl">
  {% for (var i=0, file; file=o.files[i]; i++) { %}
    <tr class="template-download fade">
      {% if (file.error) { %}
        <td></td>
        <td class="name"><span>{%=file.name%}</span></td>
        <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
        <td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
        {% } else { %}
        <td class="preview">{% if (file.thumbnail_url) { %}
          <a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
          {% } %}</td>
        <td class="name">
          <a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
        </td>
        <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
        <td colspan="2"></td>
        {% } %}
      <td class="delete">
        <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
        </button>
        <input type="checkbox" name="delete" value="1">
      </td>
    </tr>
    {% } %}
</script>

<script type="text/javascript" charset="utf-8">
  $(function () {
      // Initialize the jQuery File Upload widget:
      $('#uploadbutton').attr('name', 'assignment[receipts_attributes][][image]');
      $('#fileupload').fileupload({
        singleFileUploads: false,
        multipart: true,
        done: function (e, data) {
          window.location = "/project_management/show_latest";
        }
      });
      // 
      // Load existing files:
      $.getJSON($('#fileupload').prop('action'), function (files) {
        var fu = $('#fileupload').data('blueimpFileupload'), 
          template;
        fu._adjustMaxNumberOfFiles(-files.length);
        console.log(files);
        template = fu._renderDownload(files)
          .appendTo($('#fileupload .files'));
        // Force reflow:
        fu._reflow = fu._transition && template.length &&
          template[0].offsetWidth;
        template.addClass('in');
        $('#loading').remove();
      });

  });

  $('#upload-all').click(function() {
    if ($('#file-table tr').length == 0)
    {
      $("#submit-button").trigger("click");
    }
    else {
      $('#upload-everything').trigger("click");
    }
  });


</script>


<% end %>

      

Check model

class Receipt < ActiveRecord::Base
  belongs_to :purchase_expense
  belongs_to :travel_receipt
  belongs_to :purchase
  belongs_to :site
  belongs_to :other_task
  belongs_to :assignment
  belongs_to :community_service_verification
  belongs_to :community_service_donation_request
  attr_accessible :image, :created_by
  mount_uploader :image, ImageUploader

end

      

Receipt controller (creation method only)

def create
@receipt = Receipt.new(params[:receipt])
respond_to do |format|
  if @receipt.save
    format.html {
      render :json => [@receipt.to_jq_upload].to_json,
      :content_type => 'text/html',
      :layout => false
    }
    format.json { render json: {files: [@receipt.to_jq_upload]}, status: :created, location: @receipt }
  else
    format.html { render action: "new" }
    format.json { render json: @receipt.errors, status: :unprocessable_entity }
  end
end
end

      

Let me know if there is more information I could provide to help.

+3


source to share





All Articles