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.
source to share
No one has answered this question yet
Check out similar questions: