Rails Form Drag and Drop Photo Uploader Carrierwave
Let's say that we have a Track application designed to display a gallery of paintings. To add together a painting to the gallery we have a form with a text field to give the painting a name and a standard file upload field.
When we submit this form our prototype is resized, cropped and added to the gallery. The file upload is handled by CarrierWave which we covered in item in episode 253. We take a Painting
model with a call to mount_uploader
and which is passed an ImageUploader class, a subclass of CarrierWave::Uploader::Base
.
course Painting < ActiveRecord::Base attr_accessible :prototype, :name mount_uploader :image, ImageUploader end
The uploader form is a fairly standard uploader. Information technology creates a thumbnail version that resizes the paradigm using RMagick and this version is displayed on the gallery page.
class ImageUploader < CarrierWave::Uploader::Base of operations include CarrierWave::RMagick # Include the Sprockets helpers for Track 3.ane+ nugget pipeline compatibility: include Sprockets::Helpers::RailsHelper include Sprockets::Helpers::IsolatedHelper storage :file def store_dir " uploads/ #{model.grade.to_s.underscore} / #{mounted_as} / #{model.id} " finish def extension_white_list %due west( jpg jpeg gif png ) end version :thumb practise process resize_to_fill: [200, 200] stop finish
Improving The Uploader
The issue with our awarding is that we tin only upload one painting at a fourth dimension. It would exist better if we could upload multiple images at once from the primary gallery page then we'll implement this. We want a file upload button on the gallery page that allows us to select multiple images then to start we'll replace the link to the new painting page with a form where we tin can upload multiple images at one time.
<h1>Painting Gallery</h1> <div id= " paintings " > <%= return @paintings %> </div> <div form= " clear " > </div> <%= form_for Painting.new do |f| %> <%= f.label :image, " Upload paintings: " %> <%= f.file_field :image %> <% end %>
If we reload the gallery folio at present we'll see the course but its file upload field will merely permit us to select a single painting. We can fix this by passing the multiple
option to our file_field
and setting it to true
.
<%= f.file_field :image, multiple: true %>
At present, in some browsers at least, nosotros can select multiple images in the file upload dialog. There is an issue with this, yet. If we view the source for the form field nosotros'll see that the proper name of the file input field has a pair of foursquare brackets at the end. This means that the images are submitted as an array to our Runway application.
<input id= " painting_image " multiple= " multiple " name= " painting[image][] " type= " file " />
Unfortunately this causes some issues with CarrierWave and we'll need to prepare it. At that place are a number of dissimilar ways that nosotros can handle this situation, the simplest is to difficult-lawmaking the name of the field.
<%= f.file_field :image, multiple: true, name: " painting[image] " %>
If we change the proper name of the Painting
model or its image
attribute we'll demand to think to change the name of this aspect, as well merely with this change in identify there'll no longer exist foursquare brackets at the end of the file field's name and any images we upload won't be sent in an array. We now accept some other problem, however, in that if we do upload multiple files only one of them will be used to create a painting. This is where a JavaScript library can come up to our help and allow us to upload multiple files at in one case. We'll use the jQuery File Upload which is a polished solution that allows us to upload multiple files at one time. With it nosotros can select a number of files then click a 'get-go upload' push button to upload the files and it will even show upload progress bars while the files are uploading.
At that place are a couple of dissimilar means that we can set up this up. The first option is known as the UI version and this works well if we simply need a quick way to get a file upload, although it works best with Twitter Bootstrap and makes a lot of decisions for united states of america. The other option is a minimal setup which uses the basic upload functionality and which leaves the user interface up to u.s.. This mode we can customize it farther and brand it fit and integrate with our existing interface and it's what we'll using in this episode.
We demand to integrate this library into the Rails nugget pipeline and for this we'll exist using the jQuery FileUpload Rails gem. This gives united states several files that we nosotros can crave to add its functionality to our awarding. To utilize it we'll add it to the assets group of our Rails application then run bundle to install it.
group :assets practice gem ' sass-rails ' , ' ~> three.2.3 ' gem ' coffee-rails ' , ' ~> iii.2.i ' # Run across https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', :platforms => :ruddy gem ' uglifier ' , ' >= i.0.3 ' gem ' jquery-fileupload-runway ' end
Next we'll alter the application'due south JavaScript manifest file to add the jquery-fileupload/basic
as demonstrated in the README.
//= crave jquery-fileupload/basic
We still need to add this functionality to the file upload course on the paintings page and we'll do this inside the paintings CoffeeScript file.
jQuery -> $('#new_painting').fileupload()
All nosotros need to do here is call fileupload()
on the new paintings class after the DOM has loaded. When we reload the page now our file upload form has some extra functionality, although it looks the same. If nosotros use the file command to select several files null appears to happen, although if we refresh the folio subsequently we'll see that the images nosotros selected have been uploaded.
Information technology'due south important to understand that when we select multiple files the form is submitted in one case for each file. This triggers the PaintingsController
's create
activeness for each file, creating a new Painting
tape each time. Nosotros'd like the new paintings to be shown in the gallery as presently as they're uploaded without the user needing to upload the folio. To accomplish this we first demand to customize the file uploader. This accepts various options and we'll apply the dataType
selection here.
jQuery -> $( ' #new_painting ' ).fileupload dataType: " script "
The dataType
option determines the type of information that the uploader expects dorsum from the server. About examples will return JSON but this means that our Rails awarding will need to generate some JSON and that we'd also demand to write some JavaScript code to render the new paintings in the gallery. This approach usually leads to duplication on the customer and server and it can be easier in these situations to work with JavaScript instead. Using script
as the information type means that we tin can return a script from the server and it will be executed afterward the file uploads. This means that nosotros demand to change the PaintingsController
'southward create
action so that it returns some JavaScript. While we could support both HTML and JavaScript formats we'll keep things elementary and just employ a JavaScript template. First we'll change the controller and so that information technology but creates a new Painting
record.
def create @painting = Painting.create(params[:painting]) terminate
Side by side nosotros'll create a new JavaScript template for this action.
<% if @painting.new_record? %> alert("Failed to upload painting: <%= j @painting.errors.full_messages.join( ' , ' ).html_safe %> "); <% else %> $("#paintings").append(" <%= j return(@painting) %> "); <% terminate %>
This template is fairly simple. Offset we cheque to meet if the painting is a new record. If this is the case then the validations have failed and so we'll brandish an alert
that shows the validation error messages. If the painting is saved successfully nosotros'll append the rendered partial for the painting to the paintings
div
so that it's added to the gallery. We tin try this out now by reloading the page then adding a couple of files. We'll utilise another feature of jQuery File Upload to do this and drag and drop the images instead of using the file upload dialog. When we practice so the new images automatically announced in the gallery without us having to reload the page.
There are some layout issues on the page and these are primarily because the paintings that nosotros've but uploaded don't have names. This is a common trouble when dealing with multiple file uploads equally the user isn't inbound any other information about the uploaded files. To piece of work around this we can fix some default values based on the files that are uploaded. We'll add a before_create
callback which will set a default name for each file based on its filename.
form Painting < ActiveRecord::Base attr_accessible :image, :name mount_uploader :image, ImageUploader before_create :default_name def default_name self.name ||= File.basename(image.filename, ' .* ' ).titleize if image stop end
Now when we upload a file its name is prepare automatically.
Adding Progress Bars
And then far the files we've been uploading appear almost instantly every bit we're running our awarding on the local machine. If nosotros were running it across a slow connection then it could be minutes before the files appear in the gallery so nosotros should provide some feedback to the user during this time. We'll do this by showing a progress bar for each image immediately after the user chooses the files to upload. This will need to exist all on the client which ways that for convenience it would be nice if nosotros had some mode to render a client-side template. To exercise this we'll go dorsum to the JavaScript manifest file and add another require
statement to it.
//= require jquery-fileupload/vendor/tmpl
This library is included within the jQuery File Upload precious stone and it will easily allow us to render out a template which is embedded inside the HTML. We'll paste the code for the template at the bottom of our alphabetize
action.
<script id= " template-upload " type= " text/ten-tmpl " > <div form= " upload " > {%=o.name%} <div grade= " progress " > <div course= " bar " style= " width: 0%; " > </div> </div> </div> </script>
We've added a script
chemical element hither with a blazon of text/x-tmpl
. We can add some HTML inside this element that nosotros tin render on control through JavaScript and use special tags to reference an object that's passed in and add dynamic content. The residuum of the code in hither is just some div
s to handle displaying the progress bar. Next we'll add add
and progress
options to fileupload
to render the template.
jQuery -> $('#new_painting').fileupload dataType: "script" add: (e, data) -> data.context = $(tmpl("template-upload", data.files[0])) $('#new_painting').append(data.context) data.submit() progress: (east, data) -> if information.context progress = parseInt(information.loaded / data.total * 100, ten) data.context.find('.bar').css('width', progress + '%')
The add
pick is passed a function and this is triggered when a new file is added. Each carve up file that is uploaded volition trigger this office and it's passed a information
object that we can use to fetch various data such as the file object that we can pass to the template which we reference by the id
we gave it. Calling tmpl
will return this template and we pass this to the jQuery function, $
. We prepare the information.context
to the result of this so that we can reference it later. Nosotros then append the template to the new_painting
course then phone call data.submit
to trigger the uploading of the file.
The progress
callback updates a the progress bar. Outset we check that the information context that we set in add
exists; if it does and so we find the progress bar and set its width depending on the electric current progress of the uploaded file.
We can endeavor this out at present. We've removed all the pictures from the gallery so that nosotros've got a make clean slate to start from. If we drag and drop a couple of pictures into the browser window we'll run into a progress bar for each one.
If we upload a file that isn't a valid image file, say a zippo
file, we'll see an alert message later the file has uploaded and it isn't a valid file type. It would be amend if we did the validation on the customer before it was uploaded to save the time and bandwidth it takes to upload. We can practice this quite hands by changing the add
callback office to this:
jQuery -> $('#new_painting').fileupload dataType: "script" add together: (e, information) -> types = /(\.|\/)(gif|jpe?g|png)$/i file = data.files[0] if types.test(file.type) || types.test(file.name) data.context = $(tmpl("template-upload", file)) $('#new_painting').append(information.context) data.submit() else alert("#{file.name} is not a gif, jpeg, or png image file") progress: (due east, information) -> if data.context progress = parseInt(data.loaded / information.total * 100, x) information.context.find('.bar').css('width', progress + '%')
Now nosotros cheque that file's type or proper noun matches a regular expression that matches a number of mutual image types before we upload it. If information technology doesn't match we'll show an fault message instead of uploading the file. Now if we try to upload something like a nada file it won't exist uploaded and we'll see a validation error bulletin straight away.
Our example is pretty much complete at present. We take a gallery where we can upload files by drag-and-driblet and nosotros have a progress bar that shows the progress of each file as it uploads. To acquire more about jQuery File Upload it's worth taking a expect at its wiki4, particularly the options page which includes a lot of information nearly the options that nosotros pass in to the uploader. In that location'south also some information about setting Rails upwards with the UI version of jQuery File Upload.
mcclellandtakether.blogspot.com
Source: http://railscasts.com/episodes/381-jquery-file-upload?view=asciicast
0 Response to "Rails Form Drag and Drop Photo Uploader Carrierwave"
Post a Comment